原子操作
此擴充功能提供了一種以線性和原子方式執行多個「操作」的方法。操作是基本 JSON:API 規格中允許的變更的序列化形式。
客戶端可以在單個請求中發送一組操作。此擴充功能保證這些操作將按順序處理,並且將完全成功或一起失敗。
URI
此擴充功能的 URI 為 https://jsonapi.dev.org.tw/ext/atomic
。
命名空間
此擴充功能使用命名空間 atomic
。
注意:JSON:API 擴充功能只能使用保留的命名空間作為前綴引入新的文件成員。
文件結構
支援此擴充功能的文件**可以**包含基本規格允許的任何頂級成員,但 data
和 included
例外,它們**一定不能**包含在內。
此外,此類文件**可以**包含以下成員之一,但不能同時包含兩者
如果存在 atomic:operations
或 atomic:results
,則**一定不能**在同一個文件中包含 errors
成員。
操作物件
操作物件**必須**包含以下成員
-
op
:一個表示要執行的操作類型的操作碼,以字串表示。值**必須**是以下其中之一"add"
:建立新的資源或關係"update"
:更新資源或關係"remove"
:移除資源或關係
操作物件**可以**包含以下成員之一,但不能同時包含兩者,以指定操作的目標
-
ref
:一個物件,**必須**包含以下成員組合之一type
和id
:指定個別資源。type
和lid
:指定在先前操作物件中已分配本地識別碼 (lid
) 的個別資源。type
、id
和relationship
:指定個別資源的關係。type
、lid
和relationship
:指定在先前操作物件中已分配本地識別碼 (lid
) 的個別資源的關係。
-
href
:一個包含 URI 參考 [RFC3986 第 4.1 節] 的字串,用於識別操作的目標。
某些特定類型的操作需要包含 ref
或 href
,如下所述。
操作物件**也可以**包含以下任何成員
-
data
:操作的「主要資料」。 -
meta
:一個包含關於操作的非標準中繼資訊的 中繼物件。
處理不同類型的操作需要不同的成員,如下所述。
結果物件
操作結果物件**可以**包含以下任何成員
-
data
:操作產生的「主要資料」。 -
meta
:一個包含關於結果的非標準中繼資訊的 中繼物件。
空結果物件 ({}
) 對於不需要返回 data
的操作是可以接受的。
處理
使用此擴充功能發送的所有 HTTP 請求**必須**使用 POST
發出。
伺服器**必須**按照它們在 atomic:operations
陣列中出現的順序執行操作。
伺服器**必須**以原子方式執行所有操作,以便執行任何操作失敗**必須**使先前操作的任何效果失效。
如果返回響應文件,伺服器**必須**使用 200 OK
響應成功的操作請求。 結果物件 的陣列**必須**在頂級 atomic:results
成員中返回。結果陣列**必須**與請求的操作陣列長度相同,並且每個結果**必須**在位置上對應於其關聯的操作。
如果不需要任何操作返回 data
,伺服器**可以**使用 204 No Content
和無響應文件響應包含操作的成功請求。
處理錯誤
如果請求中的任何操作失敗,伺服器**必須**按照 基本規格中的描述 進行響應。 **應該**返回一個或多個 錯誤物件 的陣列,每個物件都有一個 source
成員,其中包含指向請求文件中問題來源的 pointer
。
如果請求的操作格式錯誤或不完整,則伺服器**必須**響應 400 Bad Request
並**應該**包含一個文件,其中包含頂級 errors
成員,該成員包含一個錯誤物件。錯誤物件**應該**包含一個帶有指向無效操作的 pointer
的 source
成員。
如果操作格式正確,但伺服器無法處理,則伺服器**必須**響應 400 Bad Request
或更合適的錯誤響應(例如 409 Conflict
或 422 Unprocessable Entity
)並**應該**包含一個文件,其中包含頂級 errors
成員,該成員包含一個或多個提供進一步詳細資訊的錯誤物件。
處理特定操作
以下章節描述如何處理特定操作。
建立資源
建立資源的操作**可以**通過操作的 href
成員指定資源集合。
操作**必須**包含 "add"
的 op
代碼以及作為 data
的資源物件。資源物件**必須**至少包含一個 type
成員。
例如
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:operations": [{
"op": "add",
"href": "/blogPosts",
"data": {
"type": "articles",
"attributes": {
"title": "JSON API paints my bikeshed!"
}
}
}]
}
請注意,在此範例中,href
用於指定與資源的 type
不同的資源集合(在本例中為 blogPosts
)。 href
的使用對於此擴充功能完全是可選的,但個別實作可能需要它。
響應
如果伺服器能夠使用客戶端生成的 ID 建立資源,並且其表示形式與操作中的資源相同,則伺服器**可以**返回沒有 data
成員的結果,或者它**可以**返回包含已建立資源作為 data
的結果。
在所有其他伺服器能夠成功建立資源的情況下,伺服器**必須**返回包含已建立資源作為 data
的結果。
例如
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:results": [{
"data": {
"links": {
"self": "http://example.com/blogPosts/13"
},
"type": "articles",
"id": "13",
"attributes": {
"title": "JSON API paints my bikeshed!"
}
}
}]
}
如果伺服器無法建立資源,則**必須**返回適當的錯誤響應,並且**應該**返回包含頂級 errors
的響應文件,如上所述。
更新資源
更新資源的操作**可以**通過操作的 ref
或 href
成員指定該資源,但不能同時指定兩者。
操作**必須**包含 "update"
的 op
代碼。
例如
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"data": {
"type": "articles",
"id": "13",
"attributes": {
"title": "To TDD or Not"
}
}
}]
}
響應
如果伺服器接受更新,但也以請求未指定的方式更改資源(例如,更新 updatedAt
屬性或計算的 sha
),則伺服器**必須**返回包含已更新資源表示形式作為 data
的結果。
如果伺服器接受更新,並且除了提供的欄位之外沒有更新任何欄位,則伺服器**必須**返回包含無 data
或資源表示形式作為 data
的結果,或者,如果所有結果都為空,則伺服器**可以**以 204 No Content
和無文件回應。
如果伺服器無法更新資源,則**必須**返回適當的錯誤回應,並且**應該**返回包含頂級 errors
的回應文件,如上所述。
刪除資源
刪除資源的操作**必須**透過操作的 ref
或 href
成員(但不能同時使用兩者)來指定該資源。
操作**必須**包含 "remove"
的 op
代碼。
例如
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:operations": [{
"op": "remove",
"ref": {
"type": "articles",
"id": "13"
}
}]
}
響應
如果伺服器能夠刪除資源,則伺服器**必須**返回沒有 data
的結果,或者,如果所有結果都為空,則伺服器**可以**以 204 No Content
和無文件回應。
如果伺服器無法刪除資源,則**必須**返回適當的錯誤回應,並且**應該**返回包含頂級 errors
的回應文件,如上所述。
更新一對一關係
更新資源的一對一關係的操作**必須**透過操作的 ref
或 href
成員(但不能同時使用兩者)來指定該關係。
操作**必須**包含 "update"
的 op
代碼。
例如,以下請求指定一對一關係
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"ref": {
"type": "articles",
"id": "13",
"relationship": "author"
},
"data": {
"type": "people",
"id": "9"
}
}]
}
以下請求清除一對一關係
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"ref": {
"type": "articles",
"id": "13",
"relationship": "author"
},
"data": null
}]
}
響應
如果伺服器能夠更新關係,則伺服器**必須**返回沒有 data
的結果,或者,如果所有結果都為空,則伺服器**可以**以 204 No Content
和無文件回應。
如果伺服器無法更新關係,則**必須**返回適當的錯誤回應,並且**應該**返回包含頂級 errors
的回應文件,如上所述。
更新一對多關係
更新資源的一對多關係的操作**必須**透過操作的 ref
或 href
成員(但不能同時使用兩者)來指定該關係。
要將成員添加到一對多關係,操作**必須**包含 "add"
的 op
代碼。例如
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:operations": [{
"op": "add",
"ref": {
"type": "articles",
"id": "1",
"relationship": "comments"
},
"data": [
{ "type": "comments", "id": "123" }
]
}]
}
要替換一對多關係的所有成員,操作**必須**包含 "update"
的 op
代碼。例如
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:operations": [{
"op": "update",
"ref": {
"type": "articles",
"id": "1",
"relationship": "tags"
},
"data": [
{ "type": "tags", "id": "2" },
{ "type": "tags", "id": "3" }
]
}]
}
要從一對多關係中移除成員,操作**必須**包含 "remove"
的 op
代碼。例如
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:operations": [{
"op": "remove",
"ref": {
"type": "articles",
"id": "1",
"relationship": "comments"
},
"data": [
{ "type": "comments", "id": "12" },
{ "type": "comments", "id": "13" }
]
}]
}
響應
如果伺服器能夠更新關係,則伺服器**必須**返回沒有 data
的結果,或者,如果所有結果都為空,則伺服器**可以**以 204 No Content
和無文件回應。
如果伺服器無法更新關係,則**必須**返回適當的錯誤回應,並且**應該**返回包含頂級 errors
的回應文件,如上所述。
處理多個操作
以上範例都執行單個操作,該操作與基本規範中的等效單個請求一致。然而,此擴展的主要價值在於它能夠線性且原子地執行多個操作。
以下範例在單個請求中添加兩個資源並在它們之間創建關係
POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:operations": [{
"op": "add",
"data": {
"type": "authors",
"id": "acb2ebd6-ed30-4877-80ce-52a14d77d470",
"attributes": {
"name": "dgeb"
}
}
}, {
"op": "add",
"data": {
"type": "articles",
"id": "bb3ad581-806f-4237-b748-f2ea0261845c",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "acb2ebd6-ed30-4877-80ce-52a14d77d470"
}
}
}
}
}]
}
伺服器可能會以下列方式回應此請求
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.dev.org.tw/ext/atomic"
{
"atomic:results": [{
"data": {
"links": {
"self": "http://example.com/authors/acb2ebd6-ed30-4877-80ce-52a14d77d470"
},
"type": "authors",
"id": "acb2ebd6-ed30-4877-80ce-52a14d77d470",
"attributes": {
"name": "dgeb"
}
}
}, {
"data": {
"links": {
"self": "http://example.com/articles/bb3ad581-806f-4237-b748-f2ea0261845c"
},
"type": "articles",
"id": "bb3ad581-806f-4237-b748-f2ea0261845c",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/bb3ad581-806f-4237-b748-f2ea0261845c/relationships/author",
"related": "http://example.com/articles/bb3ad581-806f-4237-b748-f2ea0261845c/author"
}
}
}
}
}]
}
請注意,此操作請求也可以構建為先添加作者,然後添加文章,然後添加它們之間的關係。
另請注意,本地識別碼,即
lid
成員,對於涉及多個操作的請求特別有用。本地識別碼可用於關聯尚未分配id
的資源。