Spring MVC之@RequestBody, @ResponseBody 详解

来源:互联网 发布:数据库事务日志已满 编辑:程序博客网 时间:2024/06/06 15:04

引言:

接上一篇文章讲述处理@RequestMapping的方法参数绑定之后,详细介绍下@RequestBody、@ResponseBody的具体用法和使用时机;


简介:

@RequestBody

作用:

      i) 该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上;

      ii) 再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上。

使用时机:

A) GET、POST方式提时, 根据request header Content-Type的值来判断:

  •     application/x-www-form-urlencoded, 可选(即非必须,因为这种情况的数据@RequestParam, @ModelAttribute也可以处理,当然@RequestBody也能处理);
  •     multipart/form-data, 不能处理(即使用@RequestBody不能处理这种格式的数据);
  •     其他格式, 必须(其他格式包括application/json, application/xml等。这些格式的数据,必须使用@RequestBody来处理);

B) PUT方式提交时, 根据request header Content-Type的值来判断:

  •     application/x-www-form-urlencoded, 必须;
  •     multipart/form-data, 不能处理;
  •     其他格式, 必须;
说明:request的body部分的数据编码格式由header部分的Content-Type指定;

@ResponseBody

作用:

      该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。

使用时机:

      返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;



HttpMessageConverter

[java] view plaincopy
  1. <span style="font-family:Microsoft YaHei;">/** 
  2.  * Strategy interface that specifies a converter that can convert from and to HTTP requests and responses. 
  3.  * 
  4.  * @author Arjen Poutsma 
  5.  * @author Juergen Hoeller 
  6.  * @since 3.0 
  7.  */  
  8. public interface HttpMessageConverter<T> {  
  9.   
  10.     /** 
  11.      * Indicates whether the given class can be read by this converter. 
  12.      * @param clazz the class to test for readability 
  13.      * @param mediaType the media type to read, can be {@code null} if not specified. 
  14.      * Typically the value of a {@code Content-Type} header. 
  15.      * @return {@code true} if readable; {@code false} otherwise 
  16.      */  
  17.     boolean canRead(Class<?> clazz, MediaType mediaType);  
  18.   
  19.     /** 
  20.      * Indicates whether the given class can be written by this converter. 
  21.      * @param clazz the class to test for writability 
  22.      * @param mediaType the media type to write, can be {@code null} if not specified. 
  23.      * Typically the value of an {@code Accept} header. 
  24.      * @return {@code true} if writable; {@code false} otherwise 
  25.      */  
  26.     boolean canWrite(Class<?> clazz, MediaType mediaType);  
  27.   
  28.     /** 
  29.      * Return the list of {@link MediaType} objects supported by this converter. 
  30.      * @return the list of supported media types 
  31.      */  
  32.     List<MediaType> getSupportedMediaTypes();  
  33.   
  34.     /** 
  35.      * Read an object of the given type form the given input message, and returns it. 
  36.      * @param clazz the type of object to return. This type must have previously been passed to the 
  37.      * {@link #canRead canRead} method of this interface, which must have returned {@code true}. 
  38.      * @param inputMessage the HTTP input message to read from 
  39.      * @return the converted object 
  40.      * @throws IOException in case of I/O errors 
  41.      * @throws HttpMessageNotReadableException in case of conversion errors 
  42.      */  
  43.     T read(Class<? extends T> clazz, HttpInputMessage inputMessage)  
  44.             throws IOException, HttpMessageNotReadableException;  
  45.   
  46.     /** 
  47.      * Write an given object to the given output message. 
  48.      * @param t the object to write to the output message. The type of this object must have previously been 
  49.      * passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}. 
  50.      * @param contentType the content type to use when writing. May be {@code null} to indicate that the 
  51.      * default content type of the converter must be used. If not {@code null}, this media type must have 
  52.      * previously been passed to the {@link #canWrite canWrite} method of this interface, which must have 
  53.      * returned {@code true}. 
  54.      * @param outputMessage the message to write to 
  55.      * @throws IOException in case of I/O errors 
  56.      * @throws HttpMessageNotWritableException in case of conversion errors 
  57.      */  
  58.     void write(T t, MediaType contentType, HttpOutputMessage outputMessage)  
  59.             throws IOException, HttpMessageNotWritableException;  
  60.   
  61. }  
  62. </span>  
该接口定义了四个方法,分别是读取数据时的 canRead(), read() 和 写入数据时的canWrite(), write()方法。

在使用 <mvc:annotation-driven />标签配置时,默认配置了RequestMappingHandlerAdapter(注意是RequestMappingHandlerAdapter不是AnnotationMethodHandlerAdapter,详情查看Spring 3.1 document “16.14 Configuring Spring MVC”章节),并为他配置了一下默认的HttpMessageConverter:

[java] view plaincopy
  1. ByteArrayHttpMessageConverter converts byte arrays.  
  2.   
  3. StringHttpMessageConverter converts strings.  
  4.   
  5. ResourceHttpMessageConverter converts to/from org.springframework.core.io.Resource for all media types.  
  6.   
  7. SourceHttpMessageConverter converts to/from a javax.xml.transform.Source.  
  8.   
  9. FormHttpMessageConverter converts form data to/from a MultiValueMap<String, String>.  
  10.   
  11. Jaxb2RootElementHttpMessageConverter converts Java objects to/from XML — added if JAXB2 is present on the classpath.  
  12.   
  13. MappingJacksonHttpMessageConverter converts to/from JSON — added if Jackson is present on the classpath.  
  14.   
  15. AtomFeedHttpMessageConverter converts Atom feeds — added if Rome is present on the classpath.  
  16.   
  17. RssChannelHttpMessageConverter converts RSS feeds — added if Rome is present on the classpath.  

ByteArrayHttpMessageConverter: 负责读取二进制格式的数据和写出二进制格式的数据;

StringHttpMessageConverter:   负责读取字符串格式的数据和写出二进制格式的数据;


ResourceHttpMessageConverter:负责读取资源文件和写出资源文件数据; 

FormHttpMessageConverter:       负责读取form提交的数据(能读取的数据格式为 application/x-www-form-urlencoded,不能读取multipart/form-data格式数据);负责写入application/x-www-from-urlencoded和multipart/form-data格式的数据;


MappingJacksonHttpMessageConverter:  负责读取和写入json格式的数据;


SouceHttpMessageConverter:                   负责读取和写入 xml 中javax.xml.transform.Source定义的数据;

Jaxb2RootElementHttpMessageConverter:  负责读取和写入xml 标签格式的数据;


AtomFeedHttpMessageConverter:              负责读取和写入Atom格式的数据;

RssChannelHttpMessageConverter:           负责读取和写入RSS格式的数据;


当使用@RequestBody和@ResponseBody注解时,RequestMappingHandlerAdapter就使用它们来进行读取或者写入相应格式的数据。


HttpMessageConverter匹配过程:

@RequestBody注解时: 根据Request对象header部分的Content-Type类型,逐一匹配合适的HttpMessageConverter来读取数据;

spring 3.1源代码如下:

[java] view plaincopy
  1. <span style="font-family:Microsoft YaHei;">private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType)  
  2.             throws Exception {  
  3.   
  4.         MediaType contentType = inputMessage.getHeaders().getContentType();  
  5.         if (contentType == null) {  
  6.             StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));  
  7.             String paramName = methodParam.getParameterName();  
  8.             if (paramName != null) {  
  9.                 builder.append(' ');  
  10.                 builder.append(paramName);  
  11.             }  
  12.             throw new HttpMediaTypeNotSupportedException(  
  13.                     "Cannot extract parameter (" + builder.toString() + "): no Content-Type found");  
  14.         }  
  15.   
  16.         List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();  
  17.         if (this.messageConverters != null) {  
  18.             for (HttpMessageConverter<?> messageConverter : this.messageConverters) {  
  19.                 allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());  
  20.                 if (messageConverter.canRead(paramType, contentType)) {  
  21.                     if (logger.isDebugEnabled()) {  
  22.                         logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType  
  23.                                 +"\" using [" + messageConverter + "]");  
  24.                     }  
  25.                     return messageConverter.read(paramType, inputMessage);  
  26.                 }  
  27.             }  
  28.         }  
  29.         throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);  
  30.     }</span>  

@ResponseBody注解时: 根据Request对象header部分的Accept属性(逗号分隔),逐一按accept中的类型,去遍历找到能处理的HttpMessageConverter;

源代码如下:

[java] view plaincopy
  1. <span style="font-family:Microsoft YaHei;">private void writeWithMessageConverters(Object returnValue,  
  2.                 HttpInputMessage inputMessage, HttpOutputMessage outputMessage)  
  3.                 throws IOException, HttpMediaTypeNotAcceptableException {  
  4.             List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();  
  5.             if (acceptedMediaTypes.isEmpty()) {  
  6.                 acceptedMediaTypes = Collections.singletonList(MediaType.ALL);  
  7.             }  
  8.             MediaType.sortByQualityValue(acceptedMediaTypes);  
  9.             Class<?> returnValueType = returnValue.getClass();  
  10.             List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();  
  11.             if (getMessageConverters() != null) {  
  12.                 for (MediaType acceptedMediaType : acceptedMediaTypes) {  
  13.                     for (HttpMessageConverter messageConverter : getMessageConverters()) {  
  14.                         if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {  
  15.                             messageConverter.write(returnValue, acceptedMediaType, outputMessage);  
  16.                             if (logger.isDebugEnabled()) {  
  17.                                 MediaType contentType = outputMessage.getHeaders().getContentType();  
  18.                                 if (contentType == null) {  
  19.                                     contentType = acceptedMediaType;  
  20.                                 }  
  21.                                 logger.debug("Written [" + returnValue + "] as \"" + contentType +  
  22.                                         "\" using [" + messageConverter + "]");  
  23.                             }  
  24.                             this.responseArgumentUsed = true;  
  25.                             return;  
  26.                         }  
  27.                     }  
  28.                 }  
  29.                 for (HttpMessageConverter messageConverter : messageConverters) {  
  30.                     allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());  
  31.                 }  
  32.             }  
  33.             throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);  
  34.         }</span>  

补充:

MappingJacksonHttpMessageConverter 调用了 objectMapper.writeValue(OutputStream stream, Object)方法,使用@ResponseBody注解返回的对象就传入Object参数内。若返回的对象为已经格式化好的json串时,不使用@RequestBody注解,而应该这样处理:
1、response.setContentType("application/json; charset=UTF-8");
2、response.getWriter().print(jsonStr);
直接输出到body区,然后的视图为void。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 电商35类商标被抢注怎么办 血小板太低怎么办可以吃水果吗? 微信好友删除了只记得昵称怎么办 优酷会员1080p很卡怎么办 电脑最下面的任务栏不显示怎么办 ea账号保留的问题忘了怎么办 微博超级话题账号被屏蔽怎么办 梦幻西游手机将军令号码换了怎么办 文档的内容被锁定无法修改怎么办 用户没有权限访问u盘时该怎么办? 电脑找不到ip地址连不上网怎么办 商标提前被别人注册微博昵称怎么办 扫码注册显示手机号被占用怎么办 vivo手机屏锁密码忘了怎么办 手机号注册微博手机号不用了怎么办 微博更换手机号被别人注册过怎么办 怎么有个qq注册验证码怎么办 12306手机丢了密码忘了怎么办 教师考试注册时邮箱填写错误怎么办 计算机二级注册时邮箱填错了怎么办 注册时执业范围填错了怎么办 百度号被盗了申诉不回来怎么办 斗鱼直播伴侣显示分类不可用怎么办 斗鱼黑名单显示网络加载失败怎么办 平台登录验证码只能显示一半怎么办 想改微博密码但是忘了原密码怎么办 微博账号密码都忘了怎么办 新浪博客忘记邮箱和密码忘了怎么办 新浪助学贷款邮箱密码忘了怎么办 客厅的父母乐忘记密码了怎么办 微博密码忘记了手机号也换了怎么办 想改密码旧密码忘了怎么办 商标转让转让方不配合做补证怎么办 肇事后责任方拒赔怎么办久草www 我出车祸了对方逃逸找不到人怎么办 肇事车辆怀疑是故意伤人怎么办啊 法院判的交通事故罚金没钱交怎么办 公安抓人应该行政拘留却放了怎么办 停车场把人家车刮了跑了后怕怎么办 膝盖半月板损伤可走路不疼怎么办 面试过程中遇到写的笔没水怎么办