我正在开发的项目前端和后端是完全独立的,通过配置 webpack 的 proxy 将前端请求跨域代理到后台服务。昨天发现,我前端执行post请求,后台 springmvc 的@RequestMapping接收不到对应的请求参数。开始我以为是我 proxy 配置有问题,导致 post 参数不能传到后台。然而,并不是这样…
##proxy 配置如下:
前端代码:
java 后台代码:
Request Payload VS Form Data
前端请求
我看了前端发起的请求,请求正文并不是我熟悉的Form Data,而是Request Payload。如图注意下面两个请求的Content-Type的区别。
Request Payload 请求
Form Data 请求
了解这两个的区别之前,我们先回顾下 HTTP 请求报文格式:
Request Payload 大概格式如下,请求头部的Content-Type: application/json,并且请求正文是一个 json 格式的字符串
Form Data 大概格式如下,请求头部的Content-Type: application/x-www-form-urlencoded,并且请求正文是类似 get 请求 url 的请求参数
后台处理
对于 Request Payload 请求, 必须加@RequestBody才能将请求正文解析到对应的 bean 中,且只能通过request.getReader()来获取请求正文内容
对于 Form Data 请求,无需任何注解,springmvc 会自动使用 MessageConverter 将请求参数解析到对应的 bean,且通过request.getParameter(...)能获取请求参数
解决方案
综上,我在前端选择使用 Form Data 的方式来发起请求,使用qs库将 json 对象转化为字符串 (如{name:'dahuang',age: 11}转化为name=dahuang&age=11)。
之前我以为 axios 会自动根据你的请求正文格式来选择发起 Form Data 还是 Request Payload 请求,但是执行 delete 操作时,如图的 Content-Type 却是text/plain
所以,通过通过下面的方面来解决
一个奇怪的问题
执行 delete 操作时,我将 axios 添加了 headers,content-type: 'application/x-www-form-unlencoded',请求如图,但是后台 springmvc 的@DeleteMapping接收不到请求参数,必须使用@RequestParam String id,才能接收到请求参数。看了这个回答,有人回复说这个是 tomcat 的问题而非 spring 的问题。
更新,今天遇到了一个问题,突然我的@PatchMapping也不能获取 form 表单传递的参数了。之前是可以的,然后我 google 搜到了 HttpPutFormContentFilter,然后发现这个 filter 在WebMvcAutoConfiguration里面配置的,而这个配置生效其中有一个条件是@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),恰好,我前两天整合前端代码的时候配置 springMVC 继承了WebMvcConfigurationSupport.class所以导致了该 fliter 不生效。