编辑资源

编辑或更新资源是 API 的常见目的。可以通过向相应资源发送 POSTPUTPATCH 请求来实现编辑。虽然允许 POST 将数据附加到资源的现有表示,但建议使用 PUTPATCH,因为它们传达更明确的语义。

如果更新已执行,你的服务器应响应 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 响应代码。

为了防止中间更新(在先前获取表示状态和更新之间进行的更改),应使用 ETagIf-MatchIf-Unmodified-Since 标头。

错误处理

PATCH 的规范推荐以下错误处理:

类型 错误代码
格式错误的补丁文档 400 Bad Request
不支持的补丁文档 415 Unsupported Media Type
不可处理的请求,即如果通过应用补丁,资源将变为无效 422 Unprocessable Entity
资源未找到 404 Not Found
冲突状态,即不存在的字段的重命名(移动) 409 Conflict
冲突修改,即客户端使用 If-MatchIf-Unmodified-Since 标头验证失败。如果没有可用的前提条件,则应返回后一个错误代码 412 Precondition Failed409 Conflict
并发修改,即如果请求需要在接收之前应用,请进一步请求 409 Conflict