# Spring MVC 对 RESTful 的支持
# 1. PUT、PATCH 的参数问题
RESTful API 要求服务端能响应:get / post / put / delete 请求,以对应增删改查四大功能。对此 Spring MVC 都能支持,只需要在请求处理方法头上加上:
@RequestMapping(value="/...", method = RequestMethod.GET)
或者使用:
@GetMapping("/...")
@PostMapping("/...")
@PutMapping("/...")
@DeleteMapping("/...")
就能表示该方法仅针对于特定请求方式作出响应。
默认 Spring MVC 可以接受 PUT 请求和 PATCH 请求,但是,不接受 PUT 请求和 PATCH 请求发送来参数!!!
Spring MVC 通过 @RequestParam 注解接收的请求只有两类:
接收 GET 请求提交的参数。
接收
application/x-www-form-urlencoded
方式的 POST 请求提交的参数。不支持 PUT、PATCH(也包括 DELETE)请求的参数的获取。
当然,究其原因这个锅不该 Spring MVC 背:
Servlet 的
request.getParameter("");
本身就只支持 GET 和 POST 方法传参。Servlet API 中并没有
doPatch()
方法,更谈不上获取请求参数了。
# 2. 解决方案
解决 PUT、PATCH 的参数传递的方案有两种:
# 方案一:使用特定 Filter 处理
Spring MVC 从 3.1
开始提供了一个 Filter(过滤器)来解决这个传参问题。
从
3.1
开始,提供了一个名为 HttpPutFormContentFilter;从
5.1
开始,又提供了一个名为 FormContentFilter 的新的 Filter 来替代 HttpPutFormContentFilter 。which is the same but also handles DELETE` 。
<filter> <!-- 默认 Spring MVC 无法接受 PUT 请求参数 -->
<filter-name>httpPutFormContentFilter</filter-name>
<filter-class>
org.springframework.web.filter.HttpPutFormContentFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>httpPutFormContentFilter</filter-name>
<url-pattern>/*</url-pattern>
<!-- 注意匹配规则,这里是 /* ,表示拦截所有请求 -->
</filter-mapping>
这两个过滤器会拦截所有的 PUT 和 PATCH 请求,将它们的底层处理方式转变为与 POST 请求相同的处理方式。这样你才可以在 PUT 和 PATCH 请求的处理方法中使用 @RequestParm 注解。(否则,该注解使用无效)。
# 方法二:使用 application/json 曲线救国
PUT 和 PATCH 的请求参数默认是以 x-www-form-urlencoded
的 contentType
来发送信息,即请求参数是放在 request 的 body 中,以 aaa=xxx&bbb=yyy&ccc=zzz
。
在默认情况下,@ReqeustParam 是不会去获取它们在 body 中的请求参数的(除非采用方案一,挂羊头卖狗肉)。
方案二的本质就是索性 不使用 @RequestParam
获取参数,而使用 @RequestBody
。
利用 application/json
方式传递请求参数,将参数以 JSON 格式字符串的形式放在 request 的 body 中,在 Controller 中再配合 @RequestBody
进行参数绑定,从而获取参数。绕开 @RequestParam / request.getParam() 的限制。
# 3. 批量删除时传入多个 id 实现
# 方案一:@PathVariable
接收用数组,调用 api 时,url 后面可跟多个 id,用逗号隔开,如:localhost/user/1234,1235,1236
@DeleteMapping("/{id}")
public JsonData delete(@PathVariable("id") String[] userIds) {
userService.delete(userIds);
return JsonData.ok();
}
# 方案二:利用 FormContentFilter 强用 @RequestParam
在使用 FormContentFilter 后,DELETE 请求的底层处理方式会被 Spring MVC 偷换成 POST 方式,这样,实际上就完全可以像 POST 请求那样传递多 id 。
@DeleteMapping("/delete")
public String delete(int[] ids) {
log.info("[{}]", Arrays.toString(ids));
return "hello";
}
客户端发出的请求参数类似是:?ids=1&ids=2&ids=3
。
# 4. ResponseEntity<T> 类型
在有的 Rest-ful 风格的项目中,是通过设置响应状态码来告知请求方请求是否成功。这种情况下,自然就不能始终使用默认的 200 作为响应状态码,而是要手动设置一个符合逻辑的状态码。
ResponseEntity 返回值类型能实现这样的功能。Spring MVC 中返回的 ResponseEntity 对象,一方面包含了 HTTP 响应信息,另一方面还包含返回给请求方的数据。
Spring MVC 发现你的方法返回的是 ResponseEntity 类型的对象时,它会做如下 2 件事情:
- 将 ResponseEntity 对象中所设置的『响应状态码』信息作为本次 HTTP 请求地响应状态码。
- 将 ResponseEntity 对象中所设置的『返回数据』转换为 JSON 格式字符串后,携带在 HTTP 响应地 Body 中返回。
@RequestMapping("/test1")
public ResponseEntity<List<Department>> test1(){
List<Department> list = ...;
return ResponseEntity.ok(list); // 等同于
// return ResponseEntity.status(HttpStatus.OK).body(dept);
}
@RequestMapping("/test2")
public ResponseEntity<String> test2(){
return ResponseEntity.badRequest().body("..."); // 等同于
// return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(...)
}
再强调一遍,标注了 @ResponseEntity 注解,就不需要再标注 @ResponseBody 注解 。