spring MVC 註解處理分析(一) @ResponseBody
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 處理返回結果:
/** * 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(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 註解處理分析(一) @ResponseBody
spring的Controller的方法總的來說返回方式有兩種:一種是view, 另一種是converter,通過@ResponseBody來註解方法, 這裡主要講述註解方式,@RequestMapping的 headers 和produces引數設定不同,返回處理略有不同
Spring mvc請求處理流程詳解(一)之檢視解析
前言 Spring mvc框架相信很多人都很熟悉了,關於這方面的資料也是一搜一大把。但是感覺講的都不是很細緻,讓很多初學者都雲裡霧裡的。本人也是這樣,之前研究過,但是後面一段時間不用發現又忘記了。所以決定寫下來,以備後用。 本系列文基於spring-
Spring MVC請求處理流程分析
一、簡介 Spring MVC框架在工作中經常用到,配置簡單,使用起來也很方便,很多書籍和部落格都有介紹其處理流程,但是,對於
Spring MVC新手教程(一)
erp -s sil troy .html 解釋 rand rtu wire 直接幹貨 model 考慮給用戶展示什麽。關註支撐業務的信息構成。構建成模型。 control 調用業務邏輯產生合適的數據以及傳遞數據給視圖用於呈獻; view怎樣對數據進行布局,以
Spring MVC異常處理實例
bsp ips etag label 視圖 uri _id integer ive 以下內容引用自http://wiki.jikexueyuan.com/project/spring/mvc-framework/spring-exception-handling-examp
Spring MVC 註解相關
註解 com tst file null key comment mvc ppi // required=false表示不傳的話,會給參數賦值為null,required=true就是必須要有 @ResponseBody
Spring MVC異常處理SimpleMappingExceptionResolver
bean pri 分享圖片 ESS bsh trace 內部實現 ont per Spring MVC異常處理SimpleMappingExceptionResolver【轉】 (2012-12-07 13:45:33) 轉載▼ 標簽: 雜談 分類: 技術
Spring MVC 異常處理 - ResponseStatusExceptionResolver
執行 代碼 pin ces val col resolv use turn 作用在類和方法上面 更改返回的代碼和錯誤消息 類上 通過throw new UserName***Exception()拋出 @ResponseStatus(value=HttpStatus.FO
Spring系列(七) Spring MVC 異常處理
nco 部分 給定 uri too ebo intended 路徑 onf Servlet傳統異常處理 Servlet規範規定了當web應用發生異常時必須能夠指明, 並確定了該如何處理, 規定了錯誤信息應該包含的內容和展示頁面的方式.(詳細可以參考servlet規範文檔)
實戰 :Spring MVC + 註解 +SqlServer 框架搭建及詳解
原始碼下載:http://download.csdn.NET/detail/u010469432/6786687 https://blog.csdn.net/u010469432/article/details/17587699 先說一下Spring3 MVC的優點: spring&nb
Java Spring MVC專案搭建(一)——Spring MVC框架整合
轉自:https://www.cnblogs.com/eczhou/p/6287852.html 1、Java JDK及Tomcat安裝 我這裡安裝的是JDK 1.8 及 Tomcat 8,安裝步驟詳見:http://www.cnblogs.com/eczhou/p/6285248.html
spring mvc(4)處理模型資料
處理模型資料 Spring MVC 提供了以下幾種途徑輸出模型資料: – ModelAndView: 處理方法返回值型別為 ModelAndView時, 方法體即可通過該物件新增 模型資料 – Map 及 Model: 入參為org.springframework.ui.Mo
Spring重要註解用法分析
Table of Contents Spring註解 @Bean 宣告一個bean Bean依賴 - Bean Dependencies 獲取bean的生命週期回撥: 指定Bean的scope @Configuration 注入bean間依賴關係 有關基於Ja
Spring MVC 註解(上傳)筆記
一 對於spring mvc來說2.0以後大量使用註解確實簡單很多,最近在一個專案使用spring mvc遇到上傳檔案問題,由於使用了註解所以網上沒有找到相關使用註解上傳檔案的。官方文件又沒有更新都是老的,看了一些原始碼這才解決。 使用註解很簡單。 寫個例子:控制器類 FileUplo
Spring MVC 註解型別
Spring 2.5 引入了註解 基於註解的控制器的優勢 1. 一個控制器類可以處理多個動作,而一個實現了 Controller 介面的控制器只能處理一個動作 2. 基於註解的控制器的請求對映不需要儲存在配置檔案中,使用 RequestMapping 註解型別,可以對一個方法進行請求處理。 Contr
spring MVC提交處理帶檔案和非檔案表單
<form action="" method="post" enctype="multipart-form-date"> <input type="file" name="file"/> <input type="user.userName"/&g
spring mvc註解大全
1、@Controller 在SpringMVC 中,控制器Controller 負責處理由DispatcherServlet 分發的請求,它把使用者請求的資料經過業務處理層處理之後封裝成一個Model ,然後再把該Model 返回給對應的Vie
Spring MVC 註解 @RequestParam解析
在Spring MVC 後臺控制層獲取引數的方式主要有兩種,一種是requset.getParameter(“name”),另一種是用註解@Resquest.Param直接獲取。 一、基本使用獲取提交資料 後臺程式碼: @AuthPassport @RequestMapping("/publishe
Spring基於註解的方式一
Spring基於註解的方式一 Spring註解簡介 之前的時候我們學習的Spring都是基於Spring配置檔案的形式來編寫,現在很多的情況下使用SpringBoot的時候是基於註解的形式,這裡我們首先要了解的是Java中的註解是什麼意思。對於註解和註釋要做一定的區別。 首先我們
Spring MVC非同步處理-DeferedResult使用
DeferedResult處理流程 Spring mvc的控制層接收使用者的請求之後,如果要採用非同步處理,那麼就要返回DeferedResult<>泛型物件。在呼叫完控制層之後,立即回返回DeferedResult物件,此時驅動控制層的容器主執行緒,可以處理更多的請求。