引言:
接上一篇文章講述處理@RequestMapping的方法參數(shù)綁定之后,詳細介紹下@RequestBody、@ResponseBody的具體用法和使用時機;
?
簡介:
@RequestBody
作用:?
??? ? i) 該注解用于讀取Request請求的body部分數(shù)據(jù),使用系統(tǒng)默認配置的HttpMessageConverter進行解析,然后把相應的數(shù)據(jù)綁定到要返回的對象上;
??? ? ii) 再把HttpMessageConverter返回的對象數(shù)據(jù)綁定到 controller中方法的參數(shù)上。
使用時機:
A) GET、POST方式提時, 根據(jù)request header Content-Type的值來判斷:
- ??? application/x-www-form-urlencoded, 可選(即非必須,因為這種情況的數(shù)據(jù)@RequestParam, @ModelAttribute也可以處理,當然@RequestBody也能處理);
- ??? multipart/form-data, 不能處理(即使用@RequestBody不能處理這種格式的數(shù)據(jù));
- ??? 其他格式, 必須(其他格式包括application/json, application/xml等。這些格式的數(shù)據(jù),必須使用@RequestBody來處理);
B) PUT方式提交時, 根據(jù)request header Content-Type的值來判斷:
- ??? application/x-www-form-urlencoded, 必須;
- ??? multipart/form-data, 不能處理;
- ??? 其他格式, 必須;
說明:request的body部分的數(shù)據(jù)編碼格式由header部分的Content-Type指定;
?
@ResponseBody
?
作用:?
? ? ? 該注解用于將Controller的方法返回的對象,通過適當?shù)腍ttpMessageConverter轉(zhuǎn)換為指定格式后,寫入到Response對象的body數(shù)據(jù)區(qū)。
使用時機:
????? 返回的數(shù)據(jù)不是html標簽的頁面,而是其他某種格式的數(shù)據(jù)時(如json、xml等)使用;
?
?
HttpMessageConverter
[java]?view plaincopy
<span?style="font-family:Microsoft?YaHei;">/**??*?Strategy?interface?that?specifies?a?converter?that?can?convert?from?and?to?HTTP?requests?and?responses.??*??*?@author?Arjen?Poutsma??*?@author?Juergen?Hoeller??*?@since?3.0??*/??public?interface?HttpMessageConverter<T>?{????????/**??????*?Indicates?whether?the?given?class?can?be?read?by?this?converter.??????*?@param?clazz?the?class?to?test?for?readability??????*?@param?mediaType?the?media?type?to?read,?can?be?{@code?null}?if?not?specified.??????*?Typically?the?value?of?a?{@code?Content-Type}?header.??????*?@return?{@code?true}?if?readable;?{@code?false}?otherwise??????*/??????boolean?canRead(Class<?>?clazz,?MediaType?mediaType);????????/**??????*?Indicates?whether?the?given?class?can?be?written?by?this?converter.??????*?@param?clazz?the?class?to?test?for?writability??????*?@param?mediaType?the?media?type?to?write,?can?be?{@code?null}?if?not?specified.??????*?Typically?the?value?of?an?{@code?Accept}?header.??????*?@return?{@code?true}?if?writable;?{@code?false}?otherwise??????*/??????boolean?canWrite(Class<?>?clazz,?MediaType?mediaType);????????/**??????*?Return?the?list?of?{@link?MediaType}?objects?supported?by?this?converter.??????*?@return?the?list?of?supported?media?types??????*/??????List<MediaType>?getSupportedMediaTypes();????????/**??????*?Read?an?object?of?the?given?type?form?the?given?input?message,?and?returns?it.??????*?@param?clazz?the?type?of?object?to?return.?This?type?must?have?previously?been?passed?to?the??????*?{@link?#canRead?canRead}?method?of?this?interface,?which?must?have?returned?{@code?true}.??????*?@param?inputMessage?the?HTTP?input?message?to?read?from??????*?@return?the?converted?object??????*?@throws?IOException?in?case?of?I/O?errors??????*?@throws?HttpMessageNotReadableException?in?case?of?conversion?errors??????*/??????T?read(Class<??extends?T>?clazz,?HttpInputMessage?inputMessage)??????????????throws?IOException,?HttpMessageNotReadableException;????????/**??????*?Write?an?given?object?to?the?given?output?message.??????*?@param?t?the?object?to?write?to?the?output?message.?The?type?of?this?object?must?have?previously?been??????*?passed?to?the?{@link?#canWrite?canWrite}?method?of?this?interface,?which?must?have?returned?{@code?true}.??????*?@param?contentType?the?content?type?to?use?when?writing.?May?be?{@code?null}?to?indicate?that?the??????*?default?content?type?of?the?converter?must?be?used.?If?not?{@code?null},?this?media?type?must?have??????*?previously?been?passed?to?the?{@link?#canWrite?canWrite}?method?of?this?interface,?which?must?have??????*?returned?{@code?true}.??????*?@param?outputMessage?the?message?to?write?to??????*?@throws?IOException?in?case?of?I/O?errors??????*?@throws?HttpMessageNotWritableException?in?case?of?conversion?errors??????*/??????void?write(T?t,?MediaType?contentType,?HttpOutputMessage?outputMessage)??????????????throws?IOException,?HttpMessageNotWritableException;????}??</span>??
該接口定義了四個方法,分別是讀取數(shù)據(jù)時的 canRead(), read() 和 寫入數(shù)據(jù)時的canWrite(), write()方法。
在使用 <mvc:annotation-driven />標簽配置時,默認配置了RequestMappingHandlerAdapter(注意是RequestMappingHandlerAdapter不是AnnotationMethodHandlerAdapter,詳情查看Spring 3.1 document “16.14?Configuring Spring MVC”章節(jié)),并為他配置了一下默認的HttpMessageConverter:
?
[java]?view plaincopy
ByteArrayHttpMessageConverter?converts?byte?arrays.????StringHttpMessageConverter?converts?strings.????ResourceHttpMessageConverter?converts?to/from?org.springframework.core.io.Resource?for?all?media?types.????SourceHttpMessageConverter?converts?to/from?a?javax.xml.transform.Source.????FormHttpMessageConverter?converts?form?data?to/from?a?MultiValueMap<String,?String>.????Jaxb2RootElementHttpMessageConverter?converts?Java?objects?to/from?XML?—?added?if?JAXB2?is?present?on?the?classpath.????MappingJacksonHttpMessageConverter?converts?to/from?JSON?—?added?if?Jackson?is?present?on?the?classpath.????AtomFeedHttpMessageConverter?converts?Atom?feeds?—?added?if?Rome?is?present?on?the?classpath.????RssChannelHttpMessageConverter?converts?RSS?feeds?—?added?if?Rome?is?present?on?the?classpath.??
?
ByteArrayHttpMessageConverter: 負責讀取二進制格式的數(shù)據(jù)和寫出二進制格式的數(shù)據(jù);
?
StringHttpMessageConverter:?? 負責讀取字符串格式的數(shù)據(jù)和寫出二進制格式的數(shù)據(jù);
?
ResourceHttpMessageConverter:負責讀取資源文件和寫出資源文件數(shù)據(jù);?
FormHttpMessageConverter:?????? 負責讀取form提交的數(shù)據(jù)(能讀取的數(shù)據(jù)格式為 application/x-www-form-urlencoded,不能讀取multipart/form-data格式數(shù)據(jù));負責寫入application/x-www-from-urlencoded和multipart/form-data格式的數(shù)據(jù);
?
MappingJacksonHttpMessageConverter:? 負責讀取和寫入json格式的數(shù)據(jù);
?
SouceHttpMessageConverter:?????????????????? 負責讀取和寫入 xml 中javax.xml.transform.Source定義的數(shù)據(jù);
Jaxb2RootElementHttpMessageConverter:? 負責讀取和寫入xml 標簽格式的數(shù)據(jù);
?
AtomFeedHttpMessageConverter:????????????? 負責讀取和寫入Atom格式的數(shù)據(jù);
RssChannelHttpMessageConverter:?????????? 負責讀取和寫入RSS格式的數(shù)據(jù);
?
當使用@RequestBody和@ResponseBody注解時,RequestMappingHandlerAdapter就使用它們來進行讀取或者寫入相應格式的數(shù)據(jù)。
?
HttpMessageConverter匹配過程:
@RequestBody注解時: 根據(jù)Request對象header部分的Content-Type類型,逐一匹配合適的HttpMessageConverter來讀取數(shù)據(jù);
spring 3.1源代碼如下:
[java]?view plaincopy
<span?style="font-family:Microsoft?YaHei;">private?Object?readWithMessageConverters(MethodParameter?methodParam,?HttpInputMessage?inputMessage,?Class?paramType)??????????????throws?Exception?{????????????MediaType?contentType?=?inputMessage.getHeaders().getContentType();??????????if?(contentType?==?null)?{??????????????StringBuilder?builder?=?new?StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));??????????????String?paramName?=?methodParam.getParameterName();??????????????if?(paramName?!=?null)?{??????????????????builder.append('?');??????????????????builder.append(paramName);??????????????}??????????????throw?new?HttpMediaTypeNotSupportedException(??????????????????????"Cannot?extract?parameter?("?+?builder.toString()?+?"):?no?Content-Type?found");??????????}????????????List<MediaType>?allSupportedMediaTypes?=?new?ArrayList<MediaType>();??????????if?(this.messageConverters?!=?null)?{??????????????for?(HttpMessageConverter<?>?messageConverter?:?this.messageConverters)?{??????????????????allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());??????????????????if?(messageConverter.canRead(paramType,?contentType))?{??????????????????????if?(logger.isDebugEnabled())?{??????????????????????????logger.debug("Reading?["?+?paramType.getName()?+?"]?as?\""?+?contentType??????????????????????????????????+"\"?using?["?+?messageConverter?+?"]");??????????????????????}??????????????????????return?messageConverter.read(paramType,?inputMessage);??????????????????}??????????????}??????????}??????????throw?new?HttpMediaTypeNotSupportedException(contentType,?allSupportedMediaTypes);??????}</span>??
@ResponseBody注解時: 根據(jù)Request對象header部分的Accept屬性(逗號分隔),逐一按accept中的類型,去遍歷找到能處理的HttpMessageConverter;
源代碼如下:
[java]?view plaincopy
<span?style="font-family:Microsoft?YaHei;">private?void?writeWithMessageConverters(Object?returnValue,??????????????????HttpInputMessage?inputMessage,?HttpOutputMessage?outputMessage)??????????????????throws?IOException,?HttpMediaTypeNotAcceptableException?{??????????????List<MediaType>?acceptedMediaTypes?=?inputMessage.getHeaders().getAccept();??????????????if?(acceptedMediaTypes.isEmpty())?{??????????????????acceptedMediaTypes?=?Collections.singletonList(MediaType.ALL);??????????????}??????????????MediaType.sortByQualityValue(acceptedMediaTypes);??????????????Class<?>?returnValueType?=?returnValue.getClass();??????????????List<MediaType>?allSupportedMediaTypes?=?new?ArrayList<MediaType>();??????????????if?(getMessageConverters()?!=?null)?{??????????????????for?(MediaType?acceptedMediaType?:?acceptedMediaTypes)?{??????????????????????for?(HttpMessageConverter?messageConverter?:?getMessageConverters())?{??????????????????????????if?(messageConverter.canWrite(returnValueType,?acceptedMediaType))?{??????????????????????????????messageConverter.write(returnValue,?acceptedMediaType,?outputMessage);??????????????????????????????if?(logger.isDebugEnabled())?{??????????????????????????????????MediaType?contentType?=?outputMessage.getHeaders().getContentType();??????????????????????????????????if?(contentType?==?null)?{??????????????????????????????????????contentType?=?acceptedMediaType;??????????????????????????????????}??????????????????????????????????logger.debug("Written?["?+?returnValue?+?"]?as?\""?+?contentType?+??????????????????????????????????????????"\"?using?["?+?messageConverter?+?"]");??????????????????????????????}??????????????????????????????this.responseArgumentUsed?=?true;??????????????????????????????return;??????????????????????????}??????????????????????}??????????????????}??????????????????for?(HttpMessageConverter?messageConverter?:?messageConverters)?{??????????????????????allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());??????????????????}??????????????}??????????????throw?new?HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);??????????}</span>??
?
補充:
MappingJacksonHttpMessageConverter 調(diào)用了 objectMapper.writeValue(OutputStream stream, Object)方法,使用@ResponseBody注解返回的對象就傳入Object參數(shù)內(nèi)。若返回的對象為已經(jīng)格式化好的json串時,不使用@RequestBody注解,而應該這樣處理:
1、response.setContentType("application/json; charset=UTF-8");
2、response.getWriter().print(jsonStr);
直接輸出到body區(qū),然后的視圖為void。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎
總結(jié)
以上是生活随笔為你收集整理的@RequestBody、@ResponseBody的具体用法和使用时机的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。