spring mvc controller 方法参数

  • Post author:
  • Post category:其他

疑问:

1. 为什么springmvc controller的方法参数支持非常多的类型和注解类型?如何实现的?

2. 常用的@RequestBody 和 @RequestParam 有什么区别?

3. 如果需要传入 2 个pojo对象到方法,分别是Car 和 Brand,可以建一个新的对象

CarAndBrand {

  Car car;

  Brand brand;

}

然后使用 handle(@ResponseBody CarAndBrand);如果不想多建一个CarAndBrand对象,有没有更省事的方法?

 

Http协议里content-type: application/x-www-form-urlencoded, application/json, form-data

除了url中的query参数,这三种是最常用的传参格式。在http协议中分别长这个样子

form-data

form-rulencoded

 json

稍微比较一下 form-data 和 form-urlencoded:

  • form-data使用特殊的字符串分割每个key-value对,同时在传输上直接传输value的二进制。所以适合文件上传这种数据量大的功能。
  • form-urlencoded,key1=value1&key2=value2这种方式传输,而且会对value值进行ascii转码。
  • 在html代码中,普通的<form action=”xxxx”>,submit的时候,是form-urlencoded。对于复杂的form(比如有文件上传),使用<form action=”xxxx” enctype=”multipart/form-data”>,submit的时候,是form-data。

 Servlet对三种类型的处理

参考servlet 3.1规范文档,里面描述的很清楚

  1. 对于url query参数和POST + form-urlencoded,可以通过httpServeltRequest.getParameter()获取单个参数的值
  2. 对于 multipart/form-data, 可以通过httpServletRequest.getPart()获取单个参数的值

 

对于application/json,json的内容都在http request body中,可以通过httpServletRequest.getInputStream()获取整个request body的内容然后再处理。 

 

为什么springmvc controller的方法参数支持非常多的类型和注解类型?如何实现的?

参考spring mvc的文档,拿下面几种最常见的来说明(特别注意最后的 any other arguments)

@RequestParam

For access to the Servlet request parameters, including multipart files. Parameter values are converted to the declared method argument type. See @RequestParam as well as Multipart.

Note that use of @RequestParam is optional for simple parameter values. See “Any other argument”, at the end of this table.

@RequestBody

For access to the HTTP request body. Body content is converted to the declared method argument type by using HttpMessageConverter implementations. See @RequestBody.

@RequestPart

For access to a part in a multipart/form-data request, converting the part’s body with an HttpMessageConverter. See Multipart.

@ModelAttribute

For access to an existing attribute in the model (instantiated if not present) with data binding and validation applied. See @ModelAttribute as well as Model and DataBinder.

Note that use of @ModelAttribute is optional (for example, to set its attributes). See “Any other argument” at the end of this table.

Any other argument

If a method argument is not matched to any of the earlier values in this table and it is a simple type (as determined by BeanUtils#isSimpleProperty, it is a resolved as a @RequestParam. Otherwise, it is resolved as a @ModelAttribute.

参考源码,支持多种类型参数是由RequestMappingHandlerAdapter处理的。

 

常用的@RequestBody 和 @RequestParam 有什么区别?

@RequestBody获取的是http request body的内容,上面已经提过application/json的内容就在request body中,所以@RequestBody总是配合json使用。

而@RequestParam则与httpServletRequest的getParmater差不多。

 

第三个问题

显然不太可能有handle(@RequestBody car, @RequestBody brand)这种用法,因为application/json请求的request body是一段json格式的字符串,不是key-value对。

 

@RequestBody注解参数的解析过程

以这个注解为例,说明spring mvc如何支持多种类型参数的。

接口HandlerMethodArgumentResolver表示“如何解析(转化)一种类型的参数”,RequestMappingHandlerAdapter注册了多种resolver,每一种都辅助处理一种参数类型。@RequestBody对应的resolver是RequestResponseBodyMethodProcessor

而RequestResponseBodyMethodProcessor又是AbstractMessageConverterMethodProcessor的子类。AbstractMessageConverterMethodProcessor顾名思义,就是使用不同的MessageConverter来解析request body的.参考官网文档如下:

 

最有意思的@ModelAttribute

modelAttribute比我想象的强大多了。它对应的resolver是ServletModelAttributeMethodProcessor。

光是创建,就有不少功能:

  • From the model if already added by using Model.

  • From the HTTP session by using @SessionAttributes.

  • From a URI path variable passed through a Converter (see the next example).

  • From the invocation of a default constructor.

  • From the invocation of a “primary constructor” with arguments that match to Servlet request parameters. Argument names are determined through JavaBeans @ConstructorProperties or through runtime-retained parameter names in the bytecode.

An @ModelAttribute on a method argument indicates the argument should be retrieved from the model. If not present in the model, the argument should be instantiated first and then added to the model. Once present in the model, the argument’s fields should be populated from all request parameters that have matching names. This is known as data binding in Spring MVC, a very useful mechanism that saves you from having to parse each form field individually.

如果想传对象作为方法参数,除了JSON + @RequestBody,@ModelAttribute也是一种方法。

@ModelAttribute 修饰的model可以自动从request parameter中取key-value对来填充对象的属性。这个过程需要参考类WebDataBinder。

WebDataBinder是 DataBinder(spring-core)的子类,负责用request parameter填充对象的属性。DataBinder是一个很大的话题,包括validator和converter等等,所以说@ModelAttribute是一个功能强大的参数类型。下图是ServletModelAttributeMethodProcessor中使用WebDataBinder来填充属性的代码。

 

另外在函数返回的时候,可以直接返回自动创建好的modelAttribute。

 

使用JS将对象转格式

JSON:let json = JSON.Stringify(obj); httpClient.post(json);

form data:  let formData = new FormData();  formData.append(….); httpClient.post(formData);

url-encoded: 

let str = new URLSearchParams();  str.set(key, value); httpClient.post(str.toString());

 


版权声明:本文为qq_29454347原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。