编辑或更新资源是API的常见目的。编辑可以通过发送来实现POST,PUT或者PATCH请求到相应的资源。尽管POST允许将数据附加到资源的现有表示形式,但是建议使用PUT或PATCH在它们传达更明确的语义时使用它们。
200 OK如果执行了更新,或者202 Accepted尚未应用更新,则服务器应响应。如果无法完成,请选择最合适的错误代码。
PUT具有用请求中包含的有效负载替换当前表示的语义。如果有效负载与要更新的资源的当前表示形式不同,则服务器可以决定采用哪种方法。RFC7231定义服务器可以
重新配置目标资源以反映新的媒体类型
将PUT表示形式转换为与资源格式一致的格式,然后再将其保存为新资源状态
拒绝请求,并带有415 Unsupported Media Type指示目标资源仅限于特定(一组)媒体类型的响应。
包含JSON HAL表示形式的基本资源,例如...
{ "name": "Charlie Smith", "age": 39, "job_title": "Software Developer", "_links": { "self": { "href": "/users/1234" }, "employee": { "href": "http://www.acmee.com" }, "curies": [{ "name": "ea", "href": "http://www.acmee.com/docs/rels/{rel}", templated": true}], "ea:admin": [ "href": "/admin/2", "title": "Admin" ] } }
...可能会收到这样的更新请求
PUT /users/1234 HTTP/1.1 Host: http://www.acmee.com Content-Type: "application/json; charset=utf-8" Content-Length: 85 User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT) { "name": "Charlie Gold-Smith", "age": 40, "job_title": "Senior Software Developer" }
服务器现在可以用给定的请求正文替换资源的状态,还可以将内容类型从更改为application/hal+json,application/json或将JSON有效负载转换为JSON HAL表示形式,然后用转换后的内容替换资源的内容或拒绝更新由于带有415 Unsupported Media Type响应的媒体类型不正确,因此请求。
直接替换内容或先将表示形式转换为定义的表示模型,然后再将现有内容替换为转换后的内容,这是有区别的。后续GET请求将直接替换返回以下响应:
GET /users/1234 HTTP/1.1 Host: http://www.acmee.com Accept-Encoding: gzip, deflate Accept-Language: en-us User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT) ETag: "e0023aa4e" { "name": "Charlie Gold-Smith", "age": 40, "job_title": "Senior Software Developer" }
而转换然后替换的方法将返回以下表示:
GET /users/1234 HTTP/1.1 Host: http://www.acmee.com Accept-Encoding: gzip, deflate Accept-Language: en-us User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT) ETag: e0023aa4e { "name": "Charlie Gold-Smith", "age": 40, "job_title": "Senior Software Developer", "_links": { "self": { "href": "/users/1234" }, "employee": { "href": "http://www.acmee.com" }, "curies": [{ "name": "ea", "href": "http://www.acmee.com/docs/rels/{rel}", templated": true}], "ea:admin": [ "href": "/admin/2", "title": "Admin" ] } }
请注意,PUT尽管它被定义为幂等运算,但它还是有副作用的!这在RFC7231中记录为
应用于目标资源的PUT请求可能会对其他资源产生副作用。例如,文章可能具有用于标识“当前版本”(一种资源)的URI,该URI与标识每个特定版本的URI(在某一时刻与当前版本资源共享相同状态的不同资源)分离。因此,对“当前版本” URI的成功PUT请求除了更改目标资源的状态外,还可能创建新的版本资源,并且还可能导致在相关资源之间添加链接。
通常不会产生额外的日志条目,因为通常这肯定不是资源的状态。
RFC7231在部分更新中提到了这一点:
通过将状态与较大资源的一部分重叠的单独标识的资源作为目标,或通过使用专门为部分更新定义的其他方法(例如,RFC5789中定义的PATCH方法),可以进行部分内容更新。
因此,可以以两种方式执行部分更新:
使资源嵌入多个较小的子资源,并仅通过而不是整个资源来更新相应的子资源 PUT
使用PATCH并指示服务器更新什么
如果由于用户移动到其他位置而需要部分更新用户表示形式,而不是直接更新用户,则应直接更新相关资源,这反映了用户表示形式的部分更新。
在移动之前,用户具有以下表示形式
GET /users/1234 HTTP/1.1 Host: http://www.acmee.com Accept: application/hal+json; charset=utf-8 Accept-Encoding: gzip, deflate Accept-Language: en-us User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT) ETag: "e0023aa4e" { "name": "Charlie Gold-Smith", "age": 40, "job_title": "Senior Software Developer", "_links": { "self": { "href": "/users/1234" }, "employee": { "href": "http://www.acmee.com" }, "curies": [{ "name": "ea", "href": "http://www.acmee.com/docs/rels/{rel}", templated": true}], "ea:admin": [ "href": "/admin/2", "title": "Admin" ] }, "_embedded": { "ea:address": { "street": "Terrace Drive, Central Park", "zip": "NY 10024" "city": "New York", "country": "United States of America", "_links": { "self": { "href": "/address/abc" }, "google_maps": { "href": "http://maps.google.com/?ll=40.7739166,-73.970176" } } } } }
当用户移动到新位置时,她会更新自己的位置信息,如下所示:
PUT /address/abc HTTP/1.1 Host: http://www.acmee.com Content-Type: "application/json; charset=utf-8" Content-Length: 109 User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT) { "street": "Standford Ave", "zip": "CA 94306", "city": "Pablo Alto", "country": "United States of America" }
如上所述,利用现有地址资源和请求中地址资源之间媒体类型不匹配的替换前转换语义,现在更新了地址资源,从而对GET用户资源上的后续请求产生了影响。返回用户的新地址。
GET /users/1234 HTTP/1.1 Host: http://www.acmee.com Accept: application/hal+json; charset=utf-8 Accept-Encoding: gzip, deflate Accept-Language: en-us User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT) ETag: "e0023aa4e" { "name": "Charlie Gold-Smith", "age": 40, "job_title": "Senior Software Developer", "_links": { "self": { "href": "/users/1234" }, "employee": { "href": "http://www.acmee.com" }, "curies": [{ "name": "ea", "href": "http://www.acmee.com/docs/rels/{rel}", templated": true}], "ea:admin": [ "href": "/admin/2", "title": "Admin" ] }, "_embedded": { "ea:address": { "street": "Standford Ave", "zip": "CA 94306", "city": "Pablo Alto", "country": "United States of America" "_links": { "self": { "href": "/address/abc" }, "google_maps": { "href": "http://maps.google.com/?ll=37.4241311,-122.1524475" } } } } }
PATCH是在RFC5789中定义的,本身并不直接属于HTTP规范。一个常见的误解是,在PATCH请求中仅发送应部分更新的字段就足够了。因此,规格说明
PATCH方法请求将请求实体中描述的一组更改应用于由Request-URI标识的资源。变更集以一种称为“补丁文件”的格式表示,该格式由媒体类型标识。
这意味着客户端应计算将资源从状态A转换为状态B并将这些指令发送到服务器所需的必要步骤。
流行的基于JSON的修补媒体类型是JSON Patch。
如果我们的样本用户的年龄和职称发生了变化,并且应PATCH使用JSON Patch将代表用户收入的其他字段添加到局部更新,则可能如下所示:
PATCH /users/1234 HTTP/1.1 Host: http://www.acmee.com Content-Type: application/json-patch+json; charset=utf-8 Content-Length: 188 Accept: application/json If-Match: "e0023aa4e" [ { "op": "replace", "path": "/age", "value": 40 }, { "op": "replace", "path": "/job_title", "value": "Senior Software Developer" }, { "op": "add", "path": "/salery", "value": 63985.00 } ]
PATCH 可能一次更新多个资源,并且需要原子地应用更改,这意味着要么必须应用所有更改,要么根本不应用任何更改,这会给API实现者带来事务负担。
成功更新可能会返回类似这样的内容
HTTP/1.1 200 OK Location: /users/1234 Content-Type: application/json ETag: "df00eb258" { "name": "Charlie Smith", "age": 40, "job_title": "Senior Software Developer", "salary": 63985.00 }
尽管不仅限于200 OK响应代码。
为了防止在中间更新(改变-之间完成先前获取的表示状态和更新的)ETag,If-Match或If-Unmodified-Since报头应该被使用。
该规范PATCH建议以下错误处理:
类型 | 错误代码 |
---|---|
补丁文件格式错误 | 400 Bad Request |
不支持的补丁文件 | 415 Unsupported Media Type |
无法处理的请求,即,如果通过应用补丁使资源无效 | 422 Unprocessable Entity |
找不到资源 | 404 Not Found |
冲突状态,即不存在的字段的重命名(移动) | 409 Conflict |
修改冲突,即,如果客户端使用If-Match或If-Unmodified-Since标头而验证失败。如果没有前提条件,则应返回后一个错误代码 | 412 Precondition Failed 要么 409 Conflict |
并发修改,即是否需要在接受其他PATCH请求之前应用该请求 | 409 Conflict |