TimothyQiu's Blog

keep it simple stupid

如何在 RESTful API 中表示批量操作

分类:技术

作为 RESTful API 的拥趸,最常被质疑的就是「批量操作很难 RESTful 起来」,似乎找不出对应批量操作的 HTTP 动词来。

早年间看文章,不少解决方案是先创建一个临时资源表示需要批量操作的资源,然后针对这个临时资源进行操作。将批量操作拆分到两个接口,多少有些削足适履的意思。

现在再看,情况就明朗起来了:用 PATCH 方法配合 JSON Patch 就能很好地表示批量操作。

PATCH 方法平时比较少用到,即便使用,一般也是以 JSON Merge Patch 格式更新单个资源的部分字段。例如修改一篇文章的发布时间和标题、删除广告:

PATCH /articles/42 HTTP/1.1
Content-Type: application/merge-patch+json

{
    "title": "Oops!",
    "published_at": "2019-01-02T03:04:05Z",
    "advertisement": null
}

相当于是给出了资源的部分表示,要求合并服务器上的表示与客户端所发送的表示。而 JSON Patch 格式的内容则是针对修改的结构化描述,例如上面的例子就会变成:

PATCH /articles/42 HTTP/1.1
Content-Type: application/json-patch+json

[
    {"op": "replace", "path": "/title", "value": "Oops!"},
    {"op": "replace", "path": "/published_at", "value": "2019-01-02T03:04:05Z"},
    {"op": "remove", "path": "/advertisement"}
]

其中 op 表示需要进行的操作,path 则是 JSON Pointer,用来指向 URL 所表示资源中的具体某个对象。数组中的多个操作依次进行。

使用 JSON Patch 来表示对单独资源的修改有些大材小用。不过如果将 JSON Patch 应用于合集资源,就可以很方便地表示所需的批量操作了。例如批量删除 ID 为 42 和 43 的文章,同时将 ID 为 45 的文章设为隐藏,并且新建一篇文章:

PATCH /articles HTTP/1.1
Content-Type: application/json-patch+json

[
    {"op": "remove", "path": "/42"},
    {"op": "remove", "path": "/43"},
    {"op": "replace", "path": "/45/visible", "value": false},
    {"op": "add", "path": "/-", "value": {
        "title": "Start Wars",
        "content": "A long time ago, in a country far, far away..."
    }}
]

甚至有些过于批量……

上面例子中,因为 PATCH 操作的 URL 资源是 /articles,所以 JSON Pointer /42 指向的就是 /articles/42 资源;同理 /45/visible 指向 /articles/45 资源的 visible 字段。而 /- 中的 - 则是 JSON Patch 中用来表示数组末尾的特殊索引。

这样一来,就可以名正言顺地对合集资源做 PATCH,进行批量操作了 😄