GVKun编程网logo

Spring 3.2.4中带有@RequestBody的@InitBinder转义XSS(spring 请求转对象)

18

如果您想了解Spring3.2.4中带有@RequestBody的@InitBinder转义XSS和spring请求转对象的知识,那么本篇文章将是您的不二之选。我们将深入剖析Spring3.2.4中带

如果您想了解Spring 3.2.4中带有@RequestBody的@InitBinder转义XSSspring 请求转对象的知识,那么本篇文章将是您的不二之选。我们将深入剖析Spring 3.2.4中带有@RequestBody的@InitBinder转义XSS的各个方面,并为您解答spring 请求转对象的疑在这篇文章中,我们将为您介绍Spring 3.2.4中带有@RequestBody的@InitBinder转义XSS的相关知识,同时也会详细的解释spring 请求转对象的运用方法,并给出实际的案例分析,希望能帮助到您!

本文目录一览:

Spring 3.2.4中带有@RequestBody的@InitBinder转义XSS(spring 请求转对象)

Spring 3.2.4中带有@RequestBody的@InitBinder转义XSS(spring 请求转对象)

@RequestBody我的方法中有一个带注释的参数,如下所示:

@RequestMapping(value = "/courses/{courseId}/{name}/comment", method = RequestMethod.POST)@ResponseStatus(HttpStatus.OK)public @ResponseBody CommentContainer addComment(@PathVariable Long courseId,                          @ActiveAccount Account currentUser,                          @Valid @RequestBody AddCommentForm form,                          BindingResult formBinding,                          HttpServletRequest request) throws RequestValidationException {.....}

然后@InitBinder在同一个控制器中有一个带注释的方法:

@InitBinderpublic void initBinder(WebDataBinder dataBinder) {    dataBinder.registerCustomEditor(AddCommentForm.class, new StringEscapeEditor());}

StringEscapeEditor没有跑步。但是我的initBinder方法是。因此,它不会将我的表单映射到转义编辑器。读完此线程后,这似乎是正确的(似乎@RequestMapping不受的支持@InitBinder

我测试了映射@PathVariable字符串,然后我的编辑器正在工作。

这在我的应用程序中很重要,因为我的大多数绑定都是用@RequestBody它完成的,如果我可以对它应用一些自定义绑定,那就太好了。

解决此问题的最常用方法是什么?并转义我的输入数据以进行脚本攻击。

答案1

小编典典

为了逃避XSS,我建议在输出数据时进行转义,因为正确的转义取决于输出文档。

如果@ResponseBody客户端直接使用所产生的JSON响应,并且没有机会让XSS转义内容,那么可以自定义JacksonMessageConverter以对字符串执行XSS转义。

可以像这样自定义JacksonMessageConverter:

1)首先,我们创建ObjectMapper工厂,该工厂将创建我们的自定义对象映射器:

public class HtmlEscapingObjectMapperFactory implements FactoryBean<ObjectMapper> {    private final ObjectMapper objectMapper;    public HtmlEscapingObjectMapperFactory() {        objectMapper = new ObjectMapper();        objectMapper.getJsonFactory().setCharacterEscapes(new HTMLCharacterEscapes());    }    @Override    public ObjectMapper getObject() throws Exception {        return objectMapper;    }    @Override    public Class<?> getObjectType() {        return ObjectMapper.class;    }    @Override    public boolean isSingleton() {        return true;    }    public static class HTMLCharacterEscapes extends CharacterEscapes {        private final int[] asciiEscapes;        public HTMLCharacterEscapes() {            // start with set of characters known to require escaping (double-quote, backslash etc)            asciiEscapes = CharacterEscapes.standardAsciiEscapesForJSON();            // and force escaping of a few others:            asciiEscapes[''<''] = CharacterEscapes.ESCAPE_CUSTOM;            asciiEscapes[''>''] = CharacterEscapes.ESCAPE_CUSTOM;            asciiEscapes[''&''] = CharacterEscapes.ESCAPE_CUSTOM;            asciiEscapes[''"''] = CharacterEscapes.ESCAPE_CUSTOM;            asciiEscapes[''\''''] = CharacterEscapes.ESCAPE_CUSTOM;        }        @Override        public int[] getEscapeCodesForAscii() {            return asciiEscapes;        }        // and this for others; we don''t need anything special here        @Override        public SerializableString getEscapeSequence(int ch) {            return new SerializedString(StringEscapeUtils.escapeHtml4(Character.toString((char) ch)));        }    }}

(HtmlCharacterEscapes的灵感来自于这个问题:Spring MVC和Jackson
Mapper的HTML转义)

2)然后,我们注册使用自定义对象映射器的消息转换器(例如xml config中的示例):

<bean id="htmlEscapingObjectMapper"/><mvc:annotation-driven>    <mvc:message-converters>        <beanp:objectMapper-ref="htmlEscapingObjectMapper" />    </mvc:message-converters></mvc:annotation-driven>

现在,由创建的所有JSON消息@ResponseBody都应具有HTMLCharacterEscapes中指定的转义字符串。

该问题的替代解决方案:

  • 对象反序列化后,XSS可以在控制器主体中逃脱所需的操作
  • 也许XSS在输出内容之前先在客户端的javascript中转义

除了进行输出转义之外,还可以进行一些输入验证(使用标准的Spring验证方法)来阻止一些您不想输入到系统/数据库中的内容,这可能是有用的。

编辑:JavaConfig

我还没有尝试过,但是在Java配置中它应该像这样工作(您不需要上面的Factory Bean,因为在这种情况下您可以在config中设置所有内容):

@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {    super.configureMessageConverters(converters);    converters.add(buildHtmlEscapingJsonConverter());}private MappingJacksonHttpMessageConverter buildHtmlEscapingJsonConverter() {    MappingJacksonHttpMessageConverter htmlEscapingConverter = new MappingJacksonHttpMessageConverter();    ObjectMapper objectMapper = new ObjectMapper();    objectMapper.getJsonFactory().setCharacterEscapes(new HTMLCharacterEscapes());    htmlEscapingConverter.setObjectMapper(objectMapper);    return htmlEscapingConverter;       }

请注意,现在通常配置的所有其他非json默认消息转换器(例如XML转换器等)都将丢失,并且如果需要它们,则需要手动添加它们(您可以看到默认情况下处于活动状态)在第2.2节中:http : //www.baeldung.com/spring-httpmessageconverter-
rest)

180730-Spring之RequestBody的使用姿势小结

180730-Spring之RequestBody的使用姿势小结

logo

Spring之RequestBody的使用姿势小结

SpringMVC中处理请求参数有好几种不同的方式,如我们常见的下面几种

  • 根据 HttpServletRequest 对象获取
  • 根据 @PathVariable 注解获取url参数
  • 根据 @RequestParam 注解获取请求参数
  • 根据Bean的方式获取请求参数
  • 根据 @ModelAttribute 注解获取请求参数

对上面几种方式有兴趣的可以看一下这篇博文: SpringMVC之请求参数的获取方式

除了上面的几种方式之外,还有一种 @RequestBody 的使用方式,本文则主要介绍这种传参的使用姿势和相关注意事项

I. 使用姿势

1. 服务接口

借助Spring框架,使用@RequestBody并没有什么难度,很简单的就可以写一个使用case出来,如下

@Slf4j
@RestController
public class ReqBodyController {
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Req {
        private String key;
        private Integer size;
    }

    @RequestMapping(value = "/body", method = {RequestMethod.POST, RequestMethod.GET, RequestMethod.OPTIONS})
    public BaseRsp body(@RequestBody Req req) {
        log.info("req: {}", req);
        return new BaseRsp<>(req);
    }
}

看上面的实现,和我们通常的写法并无差别,无非是将以前的 @RequsetParam 注解换成 @RequsetBody 注解,而且这个注解内部只有一个filed,比RequsetParam还少

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
    // 默认参数必须存在,否则会抛一个异常
    boolean required() default true; 
}

看到上面的实现,估计也可以猜出,这个注解对于后端而言,写没啥问题,关键是如何用(具体来讲是如何给前端用)

2. 接口调用

上面写完了,接下来的重点就是如何使用了,在使用之前,有必要了解下 RequestBody 这个注解出现的原有以及应用场景(换句话说它和RequestParam有什么区别,为什么要单独的搞一个这个东西出来)

RequestBody

@requestBody注解常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml等。一般情况下来说常用其来处理application/json类型。

a. content-type定义

在进入下一步之前,有必要说一下Content-Type这个http请求头的作用了,下面一段来自其他博文,原文链接见最后

MediaType,即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。

常见媒体格式如下:

  • text/html : HTML格式
  • text/plain :纯文本格式
  • text/xml : XML格式
  • image/gif :gif图片格式
  • image/jpeg :jpg图片格式
  • image/png:png图片格式

以application开头的媒体格式类型:

  • application/xhtml+xml :XHTML格式
  • application/xml : XML数据格式
  • application/atom+xml :Atom XML聚合格式
  • application/json : JSON数据格式
  • application/pdf :pdf格式
  • application/msword : Word文档格式
  • application/octet-stream : 二进制流数据(如常见的文件下载)
  • application/x-www-form-urlencoded : <form encType=””>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)

b. content-type 实例说明

上面算是基本定义和取值,下面结合实例对典型的几种方式进行说明

  • application/x-www-form-urlencoded:数据被编码为名称/值对。这是标准的编码格式。
  • multipart/form-data: 数据被编码为一条消息,页上的每个控件对应消息中的一个部分。
  • text/plain: 数据以纯文本形式(text/json/xml/html)进行编码,其中不含任何控件或格式字符

对于前端使用而言,form表单的enctype属性为编码方式,常用有两种:application/x-www-form-urlencodedmultipart/form-data,默认为application/x-www-form-urlencoded

Get请求

发起Get请求时,浏览器用application/x-www-form-urlencoded方式,将表单数据转换成一个字符串(key1=value1&key2=value2...)拼接到url上,这就是我们常见的url带请求参数的情况

Post表单

发起post请求时,如果没有传文件,浏览器也是将form表单的数据封装成k=v的结果丢到http body中,拿开源中国的博客提交的表单为例,一个典型的post表单,上传的数据拼装在form data中,为kv结构

post

如果有传文件的场景,Content-Type类型会升级为multipart/form-data,这一块不详细展开,后面有机会再说

Post json串

post表单除了前面一种方式之外,还有一种也是我们常见的,就是讲所有的表单数据放在一个大的json串中,然后丢给后端,这里也有一个在线的实例,某电商平台的商品发表,截图如下

IMAGE

注意看上面的Request Payload,是一个大的json串,和前面差别明显

c. RequestBody请求

根据RequestBody的定义,要想访问前面定义的那个接口,使用传统的表单传递方式是不行的,curl命令测试如下

curl -X POST -d ''key=haha&size=123'' http://127.0.0.1:19533/body

后端对应的输出如下(抛了一个异常,表示@RequestBody注解修饰rest接口,不支持 Content type ''application/x-www-form-urlencoded;charset=UTF-8''

IMAGE

因此使用姿势需要显示添加请求头,传参也改变一下

curl -l -H "Content-type: application/json" -X GET -d ''{"key": "!23", "size": 10}'' http://127.0.0.1:19533/body

返回结果如下

IMAGE

3. 注意事项

a. content-type显示指定

根据前面的说明,可以知道 @RequestBody 这个注解的使用,使得REST接口接收的不再content-type为application/x-www-form-urlencoded的请求, 反而需要显示指定为application/json

b. 请求方法

RequestBody支持GET方法么?前面都是采用post提交参数,如果改成GET会怎样?

curl测试方式

curl -l -H "Content-type: application/json" -X GET -d ''{"key": "!23", "size": 10}'' http://127.0.0.1:19533/body\?key\=app

对应的后端debug截图如下,发现使用GET方式,并没有问题,依然可以获取到参数

IMAGE

换成大名鼎鼎的POSTMAN来测试

使用post方法请求时,截图如下,主要就是修改header的content-type,然后在body中添加json串格式的请求

IMAGE

然而改成get之后,body都直接灰掉了,也就是它不支持在get请求时,提交Body数据

IMAGE

url请求方式

接下来直接换成url的请求方式,看是否直接支持get请求

http://127.0.0.1:19533/body?{"key": "!23", "size": 10}

浏览器中输入时,服务器400, 换成curl方式请求,抛的是缺少RequestBody的异常,也就是说,将json串拼接到url中貌似不行(也有可能是我的使用姿势不对。。。)

小结

  • 到这里小结一下,使用RequestBody获取参数时,还是老老实实的选择POST方法比较合适,至于原因,跟大众,随主流,跟着大家的习惯走比较好

c. 参数获取

这个主要就是后端编写接口时,获取RequestBody参数的问题了,通过测试,发现在HttpServletRequest参数中,居然拿不到提交的RequestBody参数,演示如下

请求url为

curl -l -H "Content-type: application/json" -X POST -d ''{"key": "!23", "size": 10}'' http://127.0.0.1:19533/body\?url\=ddd

对应的debug截图如下,url参数可以拿到,RequestBody参数没有

IMAGE

首先声明,下面的这段分析,没有看源码,纯属于个人推断,如有问题,对被误导的朋友表示歉意,也希望对此有了解的朋友,多多批评指正

从传文件的思路出发,前端传文件给后端时,后端是基于流的方式,将上传的二进制流,写入到`MultipartFile`;而二进制流读完之后,没法再重复的读
RequestBody可能也是这么个逻辑,首先是从HttpServletRequest的Reader流中读取body参数并封装到上面的req对象,而不会像url参数一样,写回到`javax.servlet.ServletRequest#getParameterMap`

对上面的猜测做一个小小的验证,改成直接从HttpServletRequest的Reader流中获取请求body参数

@RequestMapping(value = "/body", method = {RequestMethod.POST, RequestMethod.GET, RequestMethod.OPTIONS})
public BaseRsp body(HttpServletRequest request) throws IOException {
    BufferedReader reader = request.getReader();
    StringBuilder builder = new StringBuilder();
    String line = reader.readLine();
    while (line != null) {
        builder.append(line);
        line = reader.readLine();
    }
    reader.close();

    String reqBody = builder.toString();
    Req req = JSON.parseObject(reqBody, Req.class);
    log.info("req: {}, request: {}", req, request.getParameterMap());
    return new BaseRsp<>(req);
}

验证如下

image

其实到这里,有个有意思的地方已经引起了我的好奇,那就是在Spring容器中HttpServletRequest这个东西,是怎么运转的,后面有机会再聊,此处不展开...

4. 小结

  • ReuqestBody 主要是处理json串格式的请求参数,要求使用方指定header content-type:application/json
  • RequestBody 通常要求调用方使用post请求
  • RequsetBody参数,不会放在HttpServletRequest的Map中,因此没法通过javax.servlet.ServletRequest#getParameter获取

II. 其他

0. 参考

  • SpringMVC之请求参数的获取方式
  • Http中Content-Type的详解

1. 一灰灰Blog: https://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

  • 微博地址: 小灰灰Blog
  • QQ: 一灰灰/3302797840

3. 扫描关注

小灰灰Blog&公众号

QrCode

知识星球

zhishi

<Spring>Spring MVC之@RequestBody, @ResponseBody 详解

Spring MVC之@RequestBody, @ResponseBody 详解

引言:

接上一篇文章讲述处理@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 >/** 
  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 >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 >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。

@RequestBody Spring MVC 示例

@RequestBody Spring MVC 示例

1.前端的访问请求

<script type="text/javascript">  
    $(document).ready(function(){  
        var saveDataAry=[];  
        var data1={"userName":"test","address":"gz"};  
        var data2={"userName":"ququ","address":"gr"};  
        saveDataAry.push(data1);  
        saveDataAry.push(data2);         
        $.ajax({ 
            type:"POST", 
            url:"user/saveUser", 
            dataType:"json",      
            contentType:"application/json",               
            data:JSON.stringify(saveData), 
            success:function(data){ 
                                       
            } 
         }); 
    });  
</script>

2.前端的访问请求

// 方式一:
@RequestMapping(value = "/saveUser", method = {RequestMethod.POST }}) 
    @ResponseBody  
    public void saveUser(@RequestBody List<User> users) { 
         userService.batchSave(users); 
    } 
 // 方式二:
@RequestMapping(value = "/saveUser", method = {RequestMethod.POST }}) 
    @ResponseBody  
    public void saveUser(@RequestBody List<Map<Object, Object>> maps) { 
         userService.batchSave(users); 
    } 

3.其他

[1].@RequestBody可以处理一下格式:

application/json
application/xml

[2].multipart/form-data的数据格式不能使用@RequestBody 处理

 

@RequestParam 和 @RequestBody 的在 spring boot 中的用法

@RequestParam 和 @RequestBody 的在 spring boot 中的用法

一、现象

    由于项目是前后端分离,因此后台使用的是 spring boot,做成微服务,只暴露接口。接口设计风格为 restful 的风格,在 get 请求下,后台接收参数的注解为 RequestBody 时会报错;在 post 请求下,后台接收参数的注解为 RequestParam 时也会报错。

二、原因

@RequestParam

用来处理 Content-Type: 为 application/x-www-form-urlencoded 编码的内容。(Http 协议中,如果不指定 Content-Type,则默认传递的参数就是 application/x-www-form-urlencoded 类型)

RequestParam 可以接受简单类型的属性,也可以接受对象类型。 例如

public R getCompletedInfo(@RequestParam Map<String, Object> params)
public R getCompletedInfo(@RequestParam AuditDecisionEntity auditDecisionEntity)
public R getCompletedInfo(@RequestParam String id)

都可以获取到数据。

实质是将 Request.getParameter () 中的 Key-Value 参数 Map 利用 Spring 的转化机制 ConversionService 配置,转化成参数接收对象或字段。

注意:

Content-Type: application/x-www-form-urlencoded 的请求中, 

get 方式中 queryString 的值,和 post 方式中 body data 的值都会被 Servlet 接受到并转化到 Request.getParameter () 参数集中,所以 @RequestParam 可以获取的到

@RequestBody

处理 HttpEntity 传递过来的数据,一般用来处理非 Content-Type: application/x-www-form-urlencoded 编码格式的数据。

  • GET 请求中,因为没有 HttpEntity,所以 @RequestBody 并不适用。
  • POST 请求中,通过 HttpEntity 传递的参数,必须要在请求头中声明数据的类型 Content-Type,SpringMVC 通过使用 HandlerAdapter 配置的 HttpMessageConverters 来解析 HttpEntity 中的数据,然后绑定到相应的 bean 上。

三、总结

  • 在 GET 请求中,不能使用 @RequestBody。
  • 在 POST 请求,可以使用 @RequestBody 和 @RequestParam,但是如果使用 @RequestBody,对于参数转化的配置必须统一。另贴上例子:

 get 请求

  前台:

$.getJSON(serverPath + "project/engineering/completedcheck/",{"gongchengid":GetUrlParam("id")}, function (result) {

后台:

@RequestMapping("/completedcheck")
public R completedcheck(@RequestParam Map<String, Object> params){

post 请求

前台:

$.post(serverPath + ''project/engineering/updateData'', JSON.stringify({"id":GetUrlParam("id"),"gongchengztxsz":"基本信息"}), function (result) {

后台:

@RequestMapping("/updateData")
public R updateData(@RequestBody EngineeringEntity engineering){

     

今天关于Spring 3.2.4中带有@RequestBody的@InitBinder转义XSSspring 请求转对象的讲解已经结束,谢谢您的阅读,如果想了解更多关于180730-Spring之RequestBody的使用姿势小结、Spring MVC之@RequestBody, @ResponseBody 详解、@RequestBody Spring MVC 示例、@RequestParam 和 @RequestBody 的在 spring boot 中的用法的相关知识,请在本站搜索。

本文标签: