浅析SpringMVC请求参数注解@RequestParam/@RequestBody传递FormData的问题
一、问题背景
使用SpringMVC请求注解遇到的两个小问题:
1、如果用@requestBody注解,则请求体内容类型一般要为application/json,如果其类型为multipart/form-data,则会报错:不支持的媒体类型;
2、如果用@requestParam注解,默认必须要传该参数名对应的参数,否则会报错。
二、原因分析
1、@RequestParam 用来处理 Content-Type: 为 application/x-www-form-urlencoded编码的内容。(注意:Http协议中,如果不指定Content-Type,则默认传递的参数就是application/x-www-form-urlencoded类型)
RequestParam可以接受简单类型的属性,也可以接受对象类型。 实质是将Request.getParameter() 中的Key-Value参数Map利用Spring的转化机制ConversionService配置,转化成参数接收对象或字段。
TIPS:在Content-Type: application/x-www-form-urlencoded
的请求中, get 方式中queryString的值,和post方式中 body data的值都会被Servlet接收到,并转化到Request.getParameter()参数集中,所以@RequestParam可以获取到值。
2、@RequestBody 处理 HttpEntity 实体类传递过来的数据,一般用来处理非 Content-Type: application/x-www-form-urlencoded
编码格式的数据。
(1)GET请求中,因为没有HttpEntity,所以@RequestBody并不适用。
(2)POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型 Content-Type
(一般比如前端请求的库都自动设置了,很多如 axios 都很智能,你传 对象,就自动设置为 application/json;你传的是 FormData 格式,就自动设置为 application/x-www-form-urlencoded),SpringMVC通过使用HandlerAdapter 配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上。
3、总结:
(1)在GET请求中,不能使用@RequestBody。
(2)在POST请求,可以使用@RequestBody和@RequestParam,但是如果使用@RequestBody,对于参数转化的配置必须统一。
三、解决
所以在传 FormData 时,应该使用 @RequestParam 方式
public OperationInfo saveDBDoc(@RequestParam(required = false) Integer id,
@RequestParam String title,
@RequestParam(required = false, defaultValue = "PUBLIC") String encryptLevel,
@RequestParam(required = false) MultipartFile file,
@RequestParam(required = false) MultipartFile imageUrl,
@RequestParam(required = false) Boolean isRepeat) throws Exception {
// ...
DBDocVO docVO = DBDocVO.builder()
.id(id)
.title(title)
.encryptLevel(encryptLevel)
.isRepeat(isRepeat)
.build();
// ...
}