zoukankan      html  css  js  c++  java
  • @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(HTTP消息转化器)

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


    该接口定义了四个方法,分别是读取数据时的 canRead(), read() 和 写入数据时的canWrite(), write()方法。

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

    [java] view plain copy
     
     print?
    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.  
    [java] view plain copy
     
     print?
    1. ByteArrayHttpMessageConverter: 负责读取二进制格式的数据和写出二进制格式的数据;  
    2.   
    3. StringHttpMessageConverter:   负责读取字符串格式的数据和写出二进制格式的数据;  
    4.   
    5.    
    6.   
    7. ResourceHttpMessageConverter:负责读取资源文件和写出资源文件数据;   
    8.   
    9. FormHttpMessageConverter:       负责读取form提交的数据(能读取的数据格式为 application/x-www-form-urlencoded,不能读取multipart/form-data格式数据);负责写入application/x-www-from-urlencoded和multipart/form-data格式的数据;  
    10.   
    11.    
    12.   
    13. MappingJacksonHttpMessageConverter:  负责读取和写入json格式的数据;  
    14.   
    15.    
    16.   
    17. SouceHttpMessageConverter:                   负责读取和写入 xml 中javax.xml.transform.Source定义的数据;  
    18.   
    19. Jaxb2RootElementHttpMessageConverter:  负责读取和写入xml 标签格式的数据;  
    20.   
    21.    
    22.   
    23. AtomFeedHttpMessageConverter:              负责读取和写入Atom格式的数据;  
    24.   
    25. RssChannelHttpMessageConverter:           负责读取和写入RSS格式的数据;  
    26.   
    27.    
    28.   
    29. 当使用@RequestBody和@ResponseBody注解时,RequestMappingHandlerAdapter就使用它们来进行读取或者写入相应格式的数据  

    HttpMessageConverter匹配过程:

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

    spring 3.1源代码如下:

    [java] view plain copy
     
     print?
    1. 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.     }  

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

    源代码如下:

    [java] view plain copy
     
     print?
    1. 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.         }  

    补充:

    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。

    参考资料:

    1、 Spring 3.1 Doc: 

    spring-3.1.0/docs/spring-framework-reference/html/mvc.html
    

    2、Spring 3.x MVC 入门4 -- @ResponseBody & @RequestBody

    转载地址:http://www.cnblogs.com/qq78292959/p/3760651.html

    ok 上面讲解了实现的过程,简单的可以理解为只要有对应协议的解析器,你就可以通过几行配置,几个注解完成协议——对象的转换工作。

    注意:Spring默认的json协议解析由Jackson完成,所以要添加jackson.jar包。

    使用@ResponseBody  带来的好处就是由spring 将协议转化为对象,不用我们自己再去转化 同样@RequestBody也是,比如我们在前台传递了参数而controller中使用@RequestBody 则可以直接获得pojo对象,不用我们再去获取参数,组装对象。

    下面看看小demo

    1、前端代码post提交:

    [javascript] view plain copy
     
     print?
    1. function profilep() {  
    2.         // 组装json格式数据  
    3.         var mydata = '{"name":"' + $('#name').val() + '","id":"'  
    4.                 + $('#id').val() + '","password":"' + $('#password').val()  
    5.                 + '"}';  
    6.         $.ajaxSetup({  
    7.             contentType : 'application/json'  
    8.         });   
    9.         $.post('http://localhost:18080/website2/user/save2.do', mydata,  
    10.                 function(data) {  
    11.                     alert("id: " + data.id + " name: " + data.name  
    12.                             + "password: " + data.password);  
    13.                 }, 'json');  
    14.     }  


    2、后台代码:

    [java] view plain copy
     
     print?
    1. @ResponseBody  
    2. @RequestMapping(value = "/save2.do"  ,method = RequestMethod.POST)  
    3. // 知己接收对象,因@RequestBody spring 帮我们处理了 协议到对象的这个过程  
    4. public User info2(@RequestBody User user) {  
    5.     String id = user.getId();  
    6.     String name = user.getName();  
    7.     String password = user.getPassword();  
    8.     Map<String, String> map = new HashMap<String, String>();  
    9.     map.put("id", id);  
    10.     map.put("name", name);  
    11.     map.put("password", password);  
    12.     userService.saveUser(map);  
    13.     User user2= new User(id,name,password);  
    14.     // 直接返回对象,因@ResponseBody spring 会帮我们处理对象和协议之间的转化  
    15.     return user2;  
    16.     }  



    3、上面两个都是post方法其实get方法也是一样的:

    前端代码:

    [javascript] view plain copy
     
     print?
    1. function profile() {  
    2.     var url = 'http://localhost:18080/website2/user/save.do?';  
    3.     var query = 'id=' + $('#id').val() + '&name=' + $('#name').val()  
    4.             + '&password=' + $('#password').val();  
    5.     url += query;  
    6.     $.get(url, function(data) {  
    7.         alert("id: " + data.id + " name: " + data.name + " status: "  
    8.                 + data.password);  
    9.     });  
    10. }  


    后台代码:

    [java] view plain copy
     
     print?
    1. @ResponseBody  
    2. @RequestMapping(value = "/save.do"  ,method = RequestMethod.GET)  
    3. public User info(HttpServletRequest request) {  
    4.     String id = request.getParameter("id");  
    5.     String name = request.getParameter("name");  
    6.     String password = request.getParameter("password");  
    7.     Map<String, String> map = new HashMap<String, String>();  
    8.     map.put("id", id);  
    9.     map.put("name", name);  
    10.     map.put("password", password);  
    11.     userService.saveUser(map);  
    12.     User user= new User(id,name,password);  
    13.     return user;  
    14.     }  

    上面的$.get方法是使用GET方式进行异步请求。$.post方法使用POST方式来进行异步请求。

    $.get 、$.post 方法的格式相同,这里就以$.get为列:

    $.get(url [, data] [, callback] [, type])

    url:请求的HTML页的URL地址

    data(可选):发送至服务器的key/value数据会未为QueryString附加到请求URL中

    callback(可选):载入成功时回调函数(即当Response的返回状态为success才调用)自动将请求结果和状态传递给该方法

    type(可选):服务器端返回内容的格式,包括xml,html,script,json,text和_default.

    这个格式通过上面的post方法和get方法可以看到,像post的那个方法就比较完整!

  • 相关阅读:
    MFC tab页面中获到其它页面的数据
    sqlite数据库中"Select * From XXX能查到数据,但是Select DISTINCT group From xxx Order By group却查不出来
    关闭程序出现崩溃(exe 已触发了一个断点及未加载ucrtbased.pdb)
    springboot 通用Mapper使用
    springBoot 发布war包
    springCloud Zuul网关
    springboot hystrix turbine 聚合监控
    springBoot Feign Hystrix Dashboard
    springBoot Ribbon Hystrix Dashboard
    springBoot Feign Hystrix
  • 原文地址:https://www.cnblogs.com/caoyc/p/5644730.html
Copyright © 2011-2022 走看看