背景
目前广泛使用的HTTP/1.1中定义了八个基本动作①,而PATCH属于扩展动作出现较晚②,很多容器或者库类的API都没有支持。所以我们在处理PATCH方法时常常需要使用一些额外的方法。
语义
用于更新资源的部分内容。例如,更新用户信息中的姓名字段。
和PUT对比
- 当资源存在时:
- PATCH 用于资源的部分内容的更新
- PUT 用于更新某个资源较完整的内容
- 当资源不存在时
- PATCH 可能会去创建一个新的资源,像是 saveOrUpdate
- PUT 只对已有资源进行更新操作,所以是 update 操作
实现和支持
-
支持:
- Apache HttpComponents HttpClient version 4.2 or later 支持了 PATCH
- Spring 3.2 开始支持 PATCH 方法,但要选对部署的容器
- JBoss Netty 支持 PATCH
-
不支持:
- 目前 JDK7 的 HttpURLConnection 未实现 PATCH
- TOMCAT 7 也不行
- PlayFramework 2 也不支持
在Spring中使用PATCH
背景
PATCH方法默认是以x-www-form-urlencoded③的contentType来发送信息,并且信息内容是放在request的body里。而
Springmvc在解析参数的时候使用的解析器是ServletModelAttributeMethodProcessor,该解析器不支持PATCH,当使用getParameterMap()获取body内容的时候,获取不到信息。
解决办法
1.使用查询字符串
传参:
使用POSTMAN模拟客户端发起请求:
服务端接收参数:
/**
* 更新 (客户端上送部分数据)
* patch 传递参数的方法:
* 1) 使用查询字符串
*/
@RequestMapping(path = "{id}", method = RequestMethod.PATCH)
@ResponseBody
public String patch(@PathVariable String id, UserInfo userInfo) {}
2.使用@RequestBody
+ JSON
:
Spring中使用@RequestBody来绑定数据时,使用的是RequestResponseBodyMethodProcessor
来解析参数,该解析器可以将json格式的内容装载到pojo里。而@RequestBody只支持以下几种contentType:application/json
、application/hal+json
、application/pathc+json
、application/merge-pathc+json
。示例如下:
1)使用PostMan测试
使用POSTMAN模拟客户端发起请求:
- 设置contentType为application/json
- 在body中使用JSON传参
服务端接收请求:
/**
* 更新 (客户端上送部分数据)
* patch 传递参数的方法:
* 1) @RequestBody + JSON
*/
@RequestMapping(path = "{id}", method = RequestMethod.PATCH)
@ResponseBody
public String patch(@PathVariable String id, @RequestBody UserInfo userInfo) {}
2)使用ajax作为客户端的代码
$.ajax({
url: 'http://localhost/v1/users/1',
type: 'PATCH',
contextType: 'application/json',
data: JSON.stringify({
name: '张三',
age: '18'
})
})
3. 使用表单隐藏字段(_method)
+ HiddenHttpMethodFilter
使用POSTMAN模拟客户端发起请求:
服务端代码:
- web.xml中增加filter
<!-- 隐藏方法拦截器 -->
<filter>
<filter-name>HttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- controller代码
/**
* 更新 (客户端上送部分数据)
* patch 传递参数的方法:
* 1) HiddenHttpMethodFilter + 额外字段_method
*/
@RequestMapping(path = "{id}", method = RequestMethod.PATCH)
@ResponseBody
public String patch(@PathVariable String id, UserInfo userInfo) {}
4. 使用HttpPutFormContenrFilter
④
使用POSTMAN模拟客户端发起请求:
服务端代码:
- web.xml中增加filter
<filter>
<filter-name>HttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- controller代码
/**
* 更新 (客户端上送部分数据)
* patch 传递参数的方法:
* 1) HttpPutFormContentFilter
*/
@RequestMapping(path = "{id}", method = RequestMethod.PATCH)
@ResponseBody
public String patch(@PathVariable String id, UserInfo userInfo) {}
注:
- ①: 八个动作分别为: OPTIONS、HEAD、GET、POST、PUT、DELETE、TRACE、CONNECT
- ②: 2010年3月份才成为正式方法,见“RFC 5789”
- ③:就是application/x-www-from-urlencoded,会将表单内的数据转换为键值对,比如,name=Java&age = 23
- ④: HiddenHttpMethodFilter和HttpPutFormContenrFilter的区别:
HttpPutFormContenrFilter
只能处理PUT和PATCH动作。其内部实现是获取以上两种请求的表单参数后,封装一个新的request包装类:
HiddenHttpMethodFilter
除了PUT和PATCH还能处理DELETE等动作。但是必须增加一个隐藏参数_method
,增加了客户端代码的耦合: