編輯資源

編輯或更新資源是 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