原子操作

此擴充功能提供了一種以線性和原子方式執行多個「操作」的方法。操作是基本 JSON:API 規格中允許的變更的序列化形式。

客戶端可以在單個請求中發送一組操作。此擴充功能保證這些操作將按順序處理,並且將完全成功或一起失敗。

URI

此擴充功能的 URI 為 https://jsonapi.dev.org.tw/ext/atomic

命名空間

此擴充功能使用命名空間 atomic

注意:JSON:API 擴充功能只能使用保留的命名空間作為前綴引入新的文件成員。

文件結構

支援此擴充功能的文件**可以**包含基本規格允許的任何頂級成員,但 dataincluded 例外,它們**一定不能**包含在內。

此外,此類文件**可以**包含以下成員之一,但不能同時包含兩者

  • atomic:operations - 一個或多個 操作物件 的陣列。

  • atomic:results - 一個或多個 結果物件 的陣列。

如果存在 atomic:operationsatomic:results,則**一定不能**在同一個文件中包含 errors 成員。

操作物件

操作物件**必須**包含以下成員

  • op:一個表示要執行的操作類型的操作碼,以字串表示。值**必須**是以下其中之一

    • "add":建立新的資源或關係
    • "update":更新資源或關係
    • "remove":移除資源或關係

操作物件**可以**包含以下成員之一,但不能同時包含兩者,以指定操作的目標

  • ref:一個物件,**必須**包含以下成員組合之一

    • typeid:指定個別資源。
    • typelid:指定在先前操作物件中已分配本地識別碼 (lid) 的個別資源。
    • typeidrelationship:指定個別資源的關係。
    • typelidrelationship:指定在先前操作物件中已分配本地識別碼 (lid) 的個別資源的關係。
  • href:一個包含 URI 參考 [RFC3986 第 4.1 節] 的字串,用於識別操作的目標。

某些特定類型的操作需要包含 refhref,如下所述。

操作物件**也可以**包含以下任何成員

  • data:操作的「主要資料」。

  • meta:一個包含關於操作的非標準中繼資訊的 中繼物件

處理不同類型的操作需要不同的成員,如下所述。

結果物件

操作結果物件**可以**包含以下任何成員

  • data:操作產生的「主要資料」。

  • meta:一個包含關於結果的非標準中繼資訊的 中繼物件

空結果物件 ({}) 對於不需要返回 data 的操作是可以接受的。

處理

使用此擴充功能發送的所有 HTTP 請求**必須**使用 POST 發出。

伺服器**必須**按照它們在 atomic:operations 陣列中出現的順序執行操作。

伺服器**必須**以原子方式執行所有操作,以便執行任何操作失敗**必須**使先前操作的任何效果失效。

如果返回響應文件,伺服器**必須**使用 200 OK 響應成功的操作請求。 結果物件 的陣列**必須**在頂級 atomic:results 成員中返回。結果陣列**必須**與請求的操作陣列長度相同,並且每個結果**必須**在位置上對應於其關聯的操作。

如果不需要任何操作返回 data,伺服器**可以**使用 204 No Content 和無響應文件響應包含操作的成功請求。

處理錯誤

如果請求中的任何操作失敗,伺服器**必須**按照 基本規格中的描述 進行響應。 **應該**返回一個或多個 錯誤物件 的陣列,每個物件都有一個 source 成員,其中包含指向請求文件中問題來源的 pointer

如果請求的操作格式錯誤或不完整,則伺服器**必須**響應 400 Bad Request 並**應該**包含一個文件,其中包含頂級 errors 成員,該成員包含一個錯誤物件。錯誤物件**應該**包含一個帶有指向無效操作的 pointersource 成員。

如果操作格式正確,但伺服器無法處理,則伺服器**必須**響應 400 Bad Request 或更合適的錯誤響應(例如 409 Conflict422 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 的響應文件,如上所述

更新資源

更新資源的操作**可以**通過操作的 refhref 成員指定該資源,但不能同時指定兩者。

操作**必須**包含 "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 的回應文件,如上所述

刪除資源

刪除資源的操作**必須**透過操作的 refhref 成員(但不能同時使用兩者)來指定該資源。

操作**必須**包含 "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 的回應文件,如上所述

更新一對一關係

更新資源的一對一關係的操作**必須**透過操作的 refhref 成員(但不能同時使用兩者)來指定該關係。

操作**必須**包含 "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 的回應文件,如上所述

更新一對多關係

更新資源的一對多關係的操作**必須**透過操作的 refhref 成員(但不能同時使用兩者)來指定該關係。

要將成員添加到一對多關係,操作**必須**包含 "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 的資源。