spring MVC 注解处理分析(一) @ResponseBody
2015-01-25 13:40
471 查看
spring的Controller的方法总的来说返回方式有两种:一种是view, 另一种是converter,通过@ResponseBody来注解方法,
这里主要讲述注解方式,@RequestMapping的 headers 和produces参数设置不同,返回处理略有不同,这里主要讲述下accept和produces的各种配置及优先级类型。
一. @ResponseBody 注解是如何处理消息的
首先找到ResponseBody的处理类:
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor,
继承了 org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor
方法writeWithMessageConverters 处理返回结果:
获取可生产的类型
二. 示例代码:
controller 片段:
当设置 accept 或者produces 优先按设置的处理,
当没有设置 ,按所有converter支持的MediaType来选择处理,
都会默认按写前面的来转换返回结果 或者加上q,q值越大的优先处理,比如:
如果请求中没设置accept参数,默认会json格式返回结果。
测试类方法:
设置headers的accept和不设置处理是不同的,
spring mvc 配置文件:
这里主要讲述注解方式,@RequestMapping的 headers 和produces参数设置不同,返回处理略有不同,这里主要讲述下accept和produces的各种配置及优先级类型。
一. @ResponseBody 注解是如何处理消息的
首先找到ResponseBody的处理类:
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor,
继承了 org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor
方法writeWithMessageConverters 处理返回结果:
/** * Writes the given return type to the given output message. * * @param returnValue the value to write to the output message * @param returnType the type of the value * @param inputMessage the input messages. Used to inspect the {@code Accept} header. * @param outputMessage the output message to write to * @throws IOException thrown in case of I/O errors * @throws HttpMediaTypeNotAcceptableException thrown when the conditions indicated by {@code Accept} header on * the request cannot be met by the message converters */ @SuppressWarnings("unchecked") protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException { Class<?> returnValueClass = returnValue.getClass(); HttpServletRequest servletRequest = inputMessage.getServletRequest(); // 取请求中accept的类型,无值为*/* List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest); // 见下面代码片段 List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass); // 匹配兼容的类型 Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>(); for (MediaType r : requestedMediaTypes) { for (MediaType p : producibleMediaTypes) { if (r.isCompatibleWith(p)) { compatibleMediaTypes.add(getMostSpecificMediaType(r, p)); } } } // 匹配不到就抛返回类型不支持 if (compatibleMediaTypes.isEmpty()) { throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes); } List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes); // 按quality 排序 MediaType.sortBySpecificityAndQuality(mediaTypes); MediaType selectedMediaType = null; for (MediaType mediaType : mediaTypes) { if (mediaType.isConcrete()) {// 取第一个 selectedMediaType = mediaType; break; } else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) { selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; break; } } if (selectedMediaType != null) { selectedMediaType = selectedMediaType.removeQualityValue(); for (HttpMessageConverter<?> messageConverter : messageConverters) { if (messageConverter.canWrite(returnValueClass, selectedMediaType)) { ((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage); if (logger.isDebugEnabled()) { logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]"); } return; } } } throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes); }
获取可生产的类型
/** * Returns the media types that can be produced: * <ul> * <li>The producible media types specified in the request mappings, or * <li>Media types of configured converters that can write the specific return value, or * <li>{@link MediaType#ALL} * </ul> */ @SuppressWarnings("unchecked") protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> returnValueClass) { Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); if (!CollectionUtils.isEmpty(mediaTypes)) { return new ArrayList<MediaType>(mediaTypes); }//当请求中accept没有设置,或者@RequestMapping注解中没有配置header的accept和produces, else if (!allSupportedMediaTypes.isEmpty()) { List<MediaType> result = new ArrayList<MediaType>(); for (HttpMessageConverter<?> converter : messageConverters) { //取spring配置中所有converter支持的MediaType if (converter.canWrite(returnValueClass, null)) { result.addAll(converter.getSupportedMediaTypes()); } } return result; } else { return Collections.singletonList(MediaType.ALL); } }
二. 示例代码:
controller 片段:
@Controller @RequestMapping(URIConstant.SERVICE_CONFIG) public class ConfigController { //设置produces @RequestMapping(value = "/queryConfig", method = RequestMethod.GET,<strong> produces = { "application/xml", "application/json" }</strong>) @ResponseBody public ResultVo<Config> queryConfig( @ModelAttribute CommonParameterVo commonParameterVo, @RequestParam(value = "key", required = true) String key) throws Exception { ResultVo<Config> resultVo = new ConfigVo(); Config config = new Config(); config.setConfigKey(key); config.setConfigValue("test"); resultVo.setData(config); setLogParameters(resultVo, "{key:" + key + "}"); return resultVo; } //设置headers的Accept @SuppressWarnings("rawtypes") @RequestMapping(value = "/queryConfigs", method = RequestMethod.GET, <strong>headers = "Accept=<span style="color:#FF9966;">application/json</span>,application/xml"</strong>) @ResponseBody public ResultVo queryConfigs(@ModelAttribute CommonParameterVo commonParameterVo) throws Exception { ResultVo<ListVo> resultVo = new ResultVo<ListVo>(); ListVo<Config> configs = new ListVo<>(); Config config = new Config(); config.setConfigValue("test"); config.setConfigKey("test"); configs.add(config); resultVo.setData(configs); setLogParameters(resultVo, configs); return resultVo; } // 没有设置 header accept 或者produces @SuppressWarnings({ "rawtypes", "unchecked" }) @RequestMapping(value = "/addConfig", method = RequestMethod.POST) @ResponseBody public ResultVo addConfig(@ModelAttribute CommonParameterVo commonParameterVo, @RequestBody Config config) throws Exception { ResultVo resultVo = new ConfigVo(); // TODO 调用业务处理。 省略 resultVo.setData(config); // 记录日志 setLogParameters(resultVo, config); return resultVo; } }
当设置 accept 或者produces 优先按设置的处理,
当没有设置 ,按所有converter支持的MediaType来选择处理,
MediaType.sortBySpecificityAndQuality(mediaTypes); 按Quality来排序,由于默认都是1,优先级都是一样, 所以
<strong>produces = { "application/xml", "application/json" } </strong><pre name="code" class="java"><strong>或者 headers = "Accept=application/xml, application/json"</strong>
都会默认按写前面的来转换返回结果 或者加上q,q值越大的优先处理,比如:
@RequestMapping(value = "/queryConfig", method = RequestMethod.GET, produces = { "application/xml;q=0.4", "application/json;q=0.5" })
如果请求中没设置accept参数,默认会json格式返回结果。
测试类方法:
设置headers的accept和不设置处理是不同的,
@Test public void testByGetJson() throws Exception { UrlBuilder urlBuilder = UrlBuilder.url(URIConstantTest.TEST_URL).append(false, configUrl) .append(false, "/queryConfig").queryparam(URIConstantTest.USER_PARAM) .queryparam("&key=test&language=en_US"); System.out.println(urlBuilder.build()); String result = Request.Get(urlBuilder.build()) // .addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .execute().returnContent().asString(); System.out.println(result); } @Test public void testByGetsJson() throws Exception { UrlBuilder urlBuilder = UrlBuilder.url(URIConstantTest.TEST_URL).append(false, configUrl) .append( c0da false, "/queryConfigs").queryparam(URIConstantTest.USER_PARAM) .queryparam("&key=test&language=en_US"); System.out.println(urlBuilder.build()); String result = Request.Get(urlBuilder.build()) // .addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .execute().returnContent().asString(); System.out.println(result); } @Test public void testAddConfigJson() throws Exception { String strReq = "{\"configValue\":\"testValu测试\",\"configKey\":\"test\"}"; UrlBuilder urlBuilder = UrlBuilder.url(URIConstantTest.TEST_URL).append(false, configUrl) .append(false, "/addConfig").queryparam(URIConstantTest.USER_PARAM) .queryparam("&key=test&language=en_US"); System.out.println(urlBuilder.build()); String result = Request.Post(urlBuilder.build()) // .addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .bodyString(strReq, ContentType.APPLICATION_JSON).execute().returnContent().asString(); System.out.println(result); }
spring mvc 配置文件:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> <context:property-placeholder location="classpath:app.properties" /> <context:spring-configured /> <context:annotation-config /> <!-- 定义控制器注解扫描包路径,控制器注解为 @Controller TODO --> <context:component-scan base-package="com.api.**.controller.**" use-default-filters="false"> <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation" /> </context:component-scan> <!-- Turns on support for mapping requests to Spring MVC @Controller methods Also registers default Formatters and Validators for use across all @Controllers --> <mvc:annotation-driven validator="validator"> <mvc:message-converters> <ref bean="stringHttpMessageConverter" /> <ref bean="jsonHttpMessageConverter" /> <ref bean="marshallingHttpMessageConverter" /> </mvc:message-converters> </mvc:annotation-driven> <!-- XML view using a JAXB marshaller --> <bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="marshallerProperties"> <map> <entry key="jaxb.formatted.output"> <value type="boolean">true</value> </entry> <entry key="jaxb.encoding" value="UTF-8" /> </map> </property> <property name="packagesToScan"> <list> <value>com.api.domain</value> <value>com.api.web.controller.vo</value> </list> </property> </bean> <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> <value>text/html;charset=UTF-8</value> </list> </property> </bean> <bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" /> <bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"> <constructor-arg ref="jaxb2Marshaller" /> <!-- <property name="supportedMediaTypes" value="application/xml"></property> --> <property name="supportedMediaTypes"> <util:list> <bean class="org.springframework.http.MediaType" c:type="application" c:subtype="xml" c:qualityValue="0.5"/> </util:list> </property> </bean> </beans>
相关文章推荐
- spring mvc 自定义注解ResponseEncryptBody、RequestDecryptBody统一处理加密、解密数据,供移动端使用的rest服务
- Spring MVC 利用 @ResponseBody 注解返回JSON
- Spring MVC中常用注解之@ResponseBody,@RequestBody,@PathVariable详解
- spring MVC 3.1 注解:@ResponseBody 返回json数据
- spring注解@ResponseBody处理ajax请求
- Spring MVC 注解@ResponseBody或流获取http请求body的json字符串
- Spring MVC -- @Responsebody注解、消息转换器
- spring注解@ResponseBody处理ajax请求,json数据类型
- 关于spring mvc3的注解之@ResponseBody的使用
- spring mvc3.1 @ResponseBody注解生成大量Accept-Charset
- Spring Mvc中Controller 中的@ResponseBody 注解
- Spring mvc 注解@ResponseBody 返回内容编码问题
- SpringMVC中Controller的@ResponseBody注解的分析
- Spring mvc 通过@ResponseBody注解返回数据
- Spring MVC注解之@ResponseBody
- Spring mvc使用注解@ResponseBody Ajax请求返回json报406错误
- Spring MVC 中的 @ResponseBody 注解的使用场合
- Spring mvc 注解@ResponseBody 返回内容编码问题
- spring mvc 使用@ResponseBody注解返回json字符串
- spring mvc3.1 @ResponseBody注解生成大量Accept-Charset