網際網路工程任務組 | H. Andrews, Ed. |
網路草案 | Cloudflare, Inc. |
預期狀態:資訊性 | A. Wright, Ed. |
失效日期:2018 年 5 月 24 日 | 2017 年 11 月 20 日 |
JSON 超媒體綱要:JSON 超媒體註解詞彙
draft-handrews-json-schema-hyperschema-00
JSON Schema 是一種基於 JSON 的格式,用於使用各種詞彙描述 JSON 資料。本文件指定一個詞彙,用於使用超連結註解 JSON 文件。這些超連結包含描述如何透過超媒體環境(例如 HTTP)操作和與遠端資源互動的屬性,並根據實例值判斷連結是否可用。本文件中描述的超連結序列化格式也可獨立於 JSON Schema 使用。
本草案的問題列表可在 <https://github.com/json-schema-org/json-schema-spec/issues> 中找到。
如需其他資訊,請參閱 <https://json-schema.dev.org.tw/>。
若要提供意見回饋,請使用此問題追蹤器、首頁上列出的通訊方法或電子郵件聯絡文件編輯人員。
本網路草案的提交完全符合 BCP 78 和 BCP 79 的規定。
網路草案是網際網路工程任務組 (IETF) 的工作文件。請注意,其他群組也可能將工作文件作為網路草案散佈。目前網路草案的清單位於 http://datatracker.ietf.org/drafts/current/。
網路草案是有效期最長為六個月的草案文件,隨時可能被其他文件更新、取代或作廢。將網路草案用作參考資料或將其引用為「工作中」是不適當的。
本網路草案將於 2018 年 5 月 24 日失效。
版權所有 (c) 2017 IETF Trust 和被認定為文件作者的人員。保留所有權利。
本文件受 BCP 78 和 IETF Trust 關於 IETF 文件的法律條款 (http://trustee.ietf.org/license-info) 的約束,這些條款在本文件發布之日生效。請仔細閱讀這些文件,因為它們描述了您對本文件的權利和限制。從本文件中提取的程式碼元件必須包含信託法律條款第 4.e 節中描述的簡化 BSD 授權文字,並在簡化 BSD 授權中描述的情況下提供,不提供任何保證。
JSON 超媒體綱要是一個 JSON Schema 詞彙,用於使用超連結和透過超媒體環境(例如 HTTP)處理和操作遠端 JSON 資源的指令來註解 JSON 文件。
JSON 超媒體綱要這個詞用於指使用這些關鍵字的 JSON Schema。單獨的「超媒體綱要」一詞是指本規範範圍內的 JSON 超媒體綱要。
引入用於指定連結的主要機制是連結描述物件 (LDO),它是 RFC 8288,第 2 節 [RFC8288] 中定義的抽象連結模型的序列化。
本規範將使用 JSON Schema 核心 [json-schema] 和 JSON Schema 驗證 [json-schema-validation] 規範定義的概念、語法和術語。建議讀者備妥這些規範的副本。
本文中的關鍵字「必須 (MUST)」、「不得 (MUST NOT)」、「必要 (REQUIRED)」、「應 (SHALL)」、「不得 (SHALL NOT)」、「應該 (SHOULD)」、「不應該 (SHOULD NOT)」、「建議 (RECOMMENDED)」、「可能 (MAY)」和「選擇性 (OPTIONAL)」應按照 RFC 2119 [RFC2119] 中所述的方式解釋。
JSON 超媒體綱要藉由描述如何從實例資料建構超連結,使從 JSON 文件建立超媒體系統成為可能。
JSON 實例文件和該實例的有效 application/schema+json 超媒體綱要的組合充當單一超媒體表示法。藉由允許這種分離,基於超媒體綱要的系統可以優雅地支援預期純 JSON 的應用程式,同時為支援超媒體綱要的應用程式和使用者代理程式提供完整的超媒體功能。
使用者代理程式可以藉由尋找 application/schema+json 媒體類型和表示超媒體綱要詞彙存在的「$schema」值來偵測超媒體綱要的存在。然後,使用者代理程式可以使用 JSON 超媒體綱要的實作,為綱要和實例文件的組合提供介面,作為資源的單一邏輯表示法,就像任何單一文件超媒體表示法格式一樣。
超媒體綱要允許表示法在網路上佔用更少的位元組,並將連結建構的負擔從伺服器分配給每個用戶端。除非用戶端應用程式請求該連結,否則使用者代理程式不需要建構連結。JSON 超媒體綱要也可以在伺服器端使用,以在執行階段產生其他連結序列化或表示法格式,或搶先追蹤連結以促進伺服器推送的使用。
以下是一個範例超媒體綱要,新增了一個連結,其 IANA 註冊連結關係類型為「self」,該連結是從具有名為「id」的一個已知物件欄位的實例建構的
{ "type": "object", "properties": { "id": { "type": "number", "readOnly": true } }, "links": [ { "rel": "self", "href": "thing/{id}" } ] }
如果實例為 {"id": 1234},且其根據 RFC 3986 第 5.1 節 [RFC3986] 的基本 URI 為「https://api.example.com/」,則「https://api.example.com/thing/1234」是產生的連結的目標 URI。
術語「綱要」、「實例」和「中繼綱要」應按照 JSON Schema 核心規範 [json-schema] 中的定義解釋。
術語「適用」和「附加」應按照 JSON Schema 驗證規範的第 3 節 [json-schema-validation] 中的定義解釋。
術語「連結」、「連結內容」(或「內容」)、「連結目標」(或「目標」)和「目標屬性」應按照 RFC 8288 的第 2 節 [RFC8288] 中的定義解釋。
術語「使用者代理程式」應按照 RFC 7230 的第 2.1 節 [RFC7230] 中的定義解釋,並廣泛適用於可能在超媒體系統中使用的任何協定,而不單單是 HTTP 用戶端。
本規範定義以下術語
JSON Hyper-Schema 實作能夠接收一個 hyper-schema、一個實例,在某些情況下還能接收客戶端輸入,並產生一組完全解析且有效的連結。正如 RFC 8288 第 2 節 [RFC8288] 所定義的,一個連結包含一個上下文、一個有類型的關係、一個目標,以及可選的其他目標屬性。
關係類型和目標屬性直接取自每個連結的連結描述物件。上下文和目標識別符號由 URI 範本、實例資料以及(在目標識別符號的情況下)客戶端輸入的某種組合構成。
目標始終由 URI 完全識別。由於 application/json 和許多其他可與 JSON Hyper-Schema 一起使用的媒體類型缺少 URI 片段識別符語法,因此上下文可能僅由 URI 部分識別。在這種情況下,剩餘的識別將以 JSON 指標的形式提供。
一些 IANA 註冊的連結關係類型在 JSON Hyper-Schema 文件中被賦予特定的語義。 「self」連結用於與實例文件所表示的資源進行互動,而「collection」和「item」連結則識別可以假設具有集合特定語義的資源。
JSON Hyper-Schema meta-schema 的當前 URI 是 <https://json-schema.dev.org.tw/draft-07/hyper-schema#>。
連結描述格式 [ldo] 可以在沒有 JSON Schema 的情況下使用,並且可以通過引用規範的連結描述 schema 作為使用連結的資料結構的 schema 來聲明使用此格式。規範連結描述 schema 的 URI 是: <https://json-schema.dev.org.tw/draft-07/links#>。
JSON Hyper-Schema 實作可以自由地以任何格式提供輸出。但是,定義了一種特定格式用於一致性測試套件,該格式也用於說明 「實作要求」 [implementation] 中的要點,並顯示由 範例 [examples] 產生的輸出。建議實作能夠以這種格式產生輸出,以方便測試。描述建議輸出格式的 JSON Schema 的 URI 是 <https://json-schema.dev.org.tw/draft-07/hyper-schema-output#>。
根據 JSON Schema 驗證的第 3 節 [json-schema-validation] 的定義,適用於實例中位置的所有 schema 的超 schema 關鍵字都可以與該實例一起使用。
當多個子 schema 適用於給定的子實例時,所有「link」陣列必須按任何順序合併為單個集合。結果集合中的每個物件都必須保留其自己的適用「base」值列表,按照解析順序,來自同一個 schema 和任何父 schema。
與所有 JSON Schema 關鍵字一樣,本節中描述的所有關鍵字都是可選的。最小有效的 JSON Hyper-schema 是空白物件。
如果存在,此關鍵字必須首先解析為 URI 範本 [uriTemplating],然後必須作為 URI 參考針對實例的當前 URI base 進行解析。結果必須設定為實例的新 URI base,同時處理包含「base」的子 schema 和其中的所有子 schema。
在針對「anchor」使用進行解析時,「base」範本的解析過程可能與針對「href」使用進行解析時不同,這在 URI 範本部分中有詳細說明。
schema 的「links」屬性用於將連結描述物件與實例關聯。此屬性的值必須是一個陣列,並且陣列中的項目必須是連結描述物件,如下所定義。
連結描述物件 (Link Description Object, LDO) 是 RFC 8288 第 2 節 [RFC8288] 中定義的抽象連結模型的序列化。如該文件中所述,一個連結包含一個上下文、一個關係類型、一個目標,以及可選的目標屬性。 JSON Hyper-Schema 的 LDO 提供了所有這些,以及使用 JSON Schema 以各種方式描述連結的輸入的其他功能。
由於使用 URI 範本來識別連結上下文和目標,以及在識別目標時可選地進一步使用客戶端輸入,因此 LDO 是一個連結範本,當與 JSON 實例文件一起使用時,它可以解析為多個連結。
LDO 的特定用途,通常涉及跨協定的請求和響應,被稱為操作。對於許多協定,在任何給定的連結上都可能進行多個操作。協定由目標的 URI scheme 表示。請注意,並非所有 URI scheme 都表示可用於通訊的協定,即使具有表示此類協定的 URI scheme 的資源也未必可通過該協定獲得。
連結描述物件必須是一個物件,並且必須存在 「href」 [href] 和 「rel」 [rel] 屬性。本節簡要介紹了每個關鍵字,並在文件後面提供了額外的使用說明和全面的範例。
在 JSON Hyper-Schema 中,連結的上下文資源預設是它所附加到的子實例(如 JSON Schema 驗證規範的第 3 節 [json-schema-validation] 所定義)。這通常不是整個實例文件。可以使用本節中的關鍵字更改此預設上下文。
根據實例的媒體類型,可能可以或不可能將 URI 指派給確切的預設上下文資源。特別是,application/json 沒有定義 URI 片段解析語法,因此普通 JSON 文件中的屬性或陣列元素無法由 URI 完全識別。當無法產生完整的 URI 時,上下文的位置應由實例文件的 URI 以及單獨的純字串 JSON 指標傳達。
實作必須能夠構建連結上下文的 URI,以及(如果需要完全識別),按照 RFC 6901 第 5 節 [RFC6901] 的規定,以字串表示形式的 JSON 指標來代替 URI 片段。基於 URI 範本構建 URI 的過程在 URI 範本 [uriTemplating] 部分中給出。
此屬性設定連結的上下文 URI。此屬性的值是一個 URI 範本 [RFC6570],並且產生的 URI 參考 [RFC3986] 必須針對實例的 base URI 進行解析。
URI 是使用為 「href」 [href] 屬性描述的相同過程從提供的 URI 範本計算而來的,但 「hrefSchema」 [hrefSchema] 必須不適用。與目標 URI 不同,上下文 URI 不接受使用者輸入。
此屬性會更改實例中被視為連結上下文資源的點。此屬性的值必須是以 JSON 字串表示形式的有效 JSON 指標,或是相對於預設上下文評估的有效相對 JSON 指標 [relative-json-pointer]。
雖然最好使用 「anchor」 [anchor] 關鍵字設定具有已知 URI 的替代上下文,但是由於 application/json 缺少片段識別符語法,因此通常無法使用 URI 更改 JSON 實例中的上下文。
即使在將 JSON 指標定義為片段識別符語法的「+json」媒體類型中,如果預設上下文巢狀於陣列中,也無法獲得預設上下文在該陣列中的位置索引,以便構建指向同一巢狀 JSON 物件中另一個屬性的指標。這將在範例中演示。
處理此關鍵字的結果應該是 URI 片段(如果實例的媒體類型允許這樣的片段)。否則,它必須是一個字串編碼的 JSON 指標。
連結的關係類型識別其語義。它是傳達應用程式如何與資源互動的主要方式。
關係定義通常不依賴於媒體類型,並且鼓勵使用者利用最合適的現有接受的關係定義。
此屬性的值必須是字串,並且必須是 RFC 8288 第 2.1 節中定義的單個連結關係類型。
此屬性是必需的。
「self」連結,如 RFC 4287 第 4.2.7.2 節 [RFC4287] 最初定義的,表示目標 URI 識別與連結上下文等效的資源。在 JSON Hyper-Schema 中,「self」連結必須可以從實例解析,因此不得存在「hrefSchema」。
超 schema 作者應使用「templateRequired」來確保「self」連結具有使用所需的所有實例資料。
超 schema 實作必須識別關係類型為「self」且將整個當前實例文件作為其上下文的連結,說明使用者代理如何與該實例文件表示的資源互動。
RFC 6573 [RFC6573] 定義並註冊了「item」和「collection」連結關係類型。 JSON Hyper-Schema 對這些類型表示的集合資源施加了額外的語義。
實作必須將「collection」連結的目標和「item」連結的上下文識別為集合。
超媒體中一種眾所周知的设计模式是使用集合資源來建立集合的成員,並為其提供伺服器指派的 URI。如果 URI scheme 指示的協定定義了一種適用於建立具有伺服器指派 URI 的資源的特定方法,那麼由這些連結關係類型識別的集合資源,不得為與建立集合成員的語義相衝突的方法定義語義。集合資源可以通過這種協定方法實作項目建立,並且使用者代理可以假設任何此類操作(如果存在)都具有項目建立語義。
由於此類方法將對應於 JSON Hyper-Schema 的資料提交概念,因此連結的 「submissionSchema」 [submissionSchema] 欄位應與集合項目的表示 schema 相容,如「item」連結的目標資源或「collection」連結的上下文資源的「self」連結所指示。
當沒有已註冊的關係(除了 "related" 之外)適用時,鼓勵使用者建立他們自己的擴充關係類型,如 RFC 8288 的 2.1.2 節 [RFC8288] 中所述。選擇連結關係類型 URI 的最簡單方法是使用已在使用的 URI 方案來識別系統的主要資源,或是使用人類可讀、不可取消參照的 URI 方案,例如 RFC 4151 定義的 "tag" [RFC4151]。
擴充關係類型 URI 不一定要可取消參照,即使使用允許這樣做的方案也是如此。
目標 URI 範本用於識別連結的目標,可能會使用實例資料。此外,透過 "hrefSchema" [hrefSchema],此範本可以根據客戶端的輸入識別一組可能使用的目標資源。解析 URI 範本的完整過程,無論有無客戶端輸入,都將在 URI 範本化 [uriTemplating] 節中介紹。
"href" 連結描述屬性的值是一個範本,用於決定相關資源的目標 URI。實例屬性的值必須相對於實例的基本 URI 解析為 URI 參考 [RFC3986]。
此屬性為必要屬性。
當解析超綱要中涉及的所有 URI 範本時,會使用本節中的關鍵字:"base"、"anchor" 和 "href"。請參閱 URI 範本化 [uriTemplating] 節,以取得完整的範本解析演算法。
請注意,當解析 "base" 範本時,解析開始的附加點是需要解析 "base" 範本的 "href" 或 "anchor" 關鍵字的附加點,而不是 "base" 關鍵字本身的附加點。
"templatePointers" 連結描述屬性的值必須是一個物件。物件中的每個屬性值必須是一個有效的 JSON 指標 [RFC6901],或是相對於正在解析範本的連結的附加點評估的有效 相對 JSON 指標 [relative-json-pointer]。
對於物件中每個與正在解析的範本中的變數名稱相符的屬性名稱,該屬性的值會調整該變數的變數解析起始位置。必須忽略與正在解析的範本中的範本變數名稱不符的屬性。
此關鍵字的值必須是一個陣列,且元素必須是唯一的。每個元素都應該符合連結 URI 範本中的變數,且不含百分比編碼。在完成整個 URI 範本解析過程後,如果此陣列中存在的任何變數沒有值,則不得使用該連結。
本節中的所有屬性僅為建議性質。雖然諸如 "title" 和 "description" 之類的關鍵字主要用於向使用者呈現連結,但那些預測連結互動或回應性質的關鍵字不得被視為權威。只要目標資源的執行階段行為與 LDO 中的目標屬性衝突,就必須尊重目標資源的執行階段行為。
此屬性定義連結的標題。該值必須是字串。
使用者代理在向使用者呈現連結時可以使用此標題。
此屬性提供標題之外的額外資訊。該值必須是字串。雖然標題最好簡短,但描述可用於更詳細地說明連結的目的和用法。
使用者代理在向使用者呈現連結時可以使用此描述。
此屬性的值表示當提取此資源時預期返回的媒體類型 RFC 2046 [RFC2046]。此屬性值也可以是媒體範圍,使用與 RFC 7231 的 5.3.2 節 - HTTP "Accept" 標頭 [RFC7231] 中定義的模式相同的模式。
此屬性類似於其他連結序列化格式的 "type" 屬性。使用者代理可以使用此資訊來告知他們在跟隨連結之前向使用者呈現的介面,但不應在解釋結果資料時使用此資訊。相反地,使用者代理必須使用回應所提供的媒體類型進行執行階段解釋。請參閱 「安全性考量」 [security] 一節,以詳細檢視對 "targetMediaType" 的濫用。
對於支援內容協商的協定,實作可以選擇使用 "headerSchema" [headerSchema] 中的協定特定資訊來描述可能的目標媒體類型。如果同時存在協定特定資訊和 "targetMediaType",則 "targetMediaType" 的值必須與協定特定資訊相容,且應指出在沒有內容協商的情況下將返回的媒體類型。
當沒有此類協定特定資訊可用時,或當實作無法識別所涉及的協定時,則該值應視為 "application/json"。
此屬性提供一個綱要,預期用於描述連結目標的表示法。根據協定,綱要可能會或可能不會描述對使用該連結執行的任何特定操作的請求或回應。請參閱 JSON 超綱要和 HTTP [HTTP] 一節,以深入討論此關鍵字如何在 HTTP 中使用。
此屬性的值僅為建議性質。它表示預期可透過與目標資源互動來發現的資訊,通常是以協定特定的控制資訊或中繼資料的形式,例如回應 HTTP HEAD 或 OPTIONS 請求時傳回的標頭。協定由 "href" URI 方案決定,但請注意,無法保證可透過此類協定存取資源。
此屬性的值應為物件。此物件的鍵應為控制資料欄位名稱的小寫形式。每個值都應為陣列,以便統一處理多值欄位。多個值必須以陣列的形式呈現,而不是以單一字串的形式呈現。
控制資訊不適合以 JSON 物件形式表示的協定可以使用其他資料類型來表示,例如陣列。
JSON 超綱要實作必須忽略無法理解為所指示協定一部分的值。應用程式可以使用這些值,但不得假定與其他實作的互通性。
實作不得假定此物件中已考量所有可探索的資訊。客戶端應用程式必須正確處理與此屬性值衝突的執行階段回應。
客戶端應用程式不得假定實作會根據此屬性的值自動採取任何動作。
請參閱 「JSON 超綱要和 HTTP」 [HTTP],以取得有關如何將此關鍵字與 HTTP 和類似協定搭配使用的指南。
有四種方法可以將客戶端輸入與連結搭配使用,每種方法都由單獨的連結描述物件關鍵字解決。在執行操作時,使用者代理應忽略與其語意無關的綱要。
"hrefSchema" 連結描述屬性的值必須是一個有效的 JSON 綱要。此綱要用於驗證使用者輸入或其他使用者代理資料,以填寫 "href" [href] 中的 URI 範本。
省略 "hrefSchema" 或將整個綱要設定為 "false" 會阻止任何使用者代理資料被接受。
將適用於特定變數的任何子綱要設定為 JSON 常值 "false" 會阻止接受該單一變數的任何使用者代理資料。
對於可以從實例資料解析的範本變數,如果實例資料針對 "hrefSchema" 中的所有適用子綱要有效,則必須使用它來預先填入該變數的輸入資料集。
請注意,即使資料是從實例預先填入的,"hrefSchema" 中該變數的驗證綱要也不必與適用於實例資料位置的驗證綱要完全相同。這允許使用者代理資料使用不同的驗證規則,例如支援日期時間輸入的拼寫月份,但對儲存使用標準的日期時間格式。
在接受輸入後,可能會覆蓋預先填入的實例資料,產生的資料集必須成功驗證 "hrefSchema" 的值。如果沒有,則不得使用該連結。如果有效,則「URI 範本化」一節中給定的過程會繼續使用此更新的資料集。
[CREF3]與 "targetHints" 一樣,此關鍵字的規格有些不足,以鼓勵實驗和回饋,因為我們試圖在彈性和清晰度之間取得平衡。
如果存在,則此屬性是協定特定的請求標頭或類似控制和中繼資料的綱要。此物件的值必須是一個有效的 JSON 綱要。協定由 "href" URI 方案決定,但請注意,無法保證可透過此類協定存取資源。綱要僅為建議性質;目標資源的行為不受其存在所約束。
此關鍵字的目的在於宣傳目標資源互動功能,並向使用者代理和客戶端應用程式指示哪些標頭和標頭值可能有用。使用者代理和客戶端應用程式可以使用綱要來驗證相關標頭,但不應假定禁止使用遺失的標頭或值。雖然綱要作者可以將 "additionalProperties" 設定為 false,但不建議這樣做,且不得阻止客戶端應用程式或使用者代理在發出請求時提供額外的標頭。
JSON 資料模型到標頭的確切對應取決於協定。但是,在大多數情況下,此綱要應指定類型為 "object",且屬性名稱應為控制資料欄位名稱的小寫形式。請參閱 「JSON 超綱要和 HTTP」 [HTTP] 一節,以取得有關如何將此關鍵字與 HTTP 和類似協定搭配使用的詳細指南。
"headerSchema" 適用於協定支援的任何請求方法或命令。在產生請求時,使用者代理和客戶端應用程式應忽略與該請求無關的標頭綱要。
在 JSON Hyper-Schema 中,"targetSchema" [targetSchema] 提供目標資源表示的非權威描述。用戶端應用程式可以使用 "targetSchema" 來建構替換或修改表示的輸入,或者作為基於修補媒體類型的修補文件之基本表示。
或者,如果 "targetSchema" 不存在,或者用戶端應用程式偏好僅使用權威資訊,則可以與目標資源互動以確認或探索其表示結構。
"targetSchema" 並不旨在描述連結操作的回應,除非回應語義表明它是目標資源的表示。在所有情況下,回應本身指示的 schema 是權威的。有關詳細範例,請參閱 "JSON Hyper-Schema 和 HTTP" [HTTP]。
"submissionSchema" [submissionSchema] 和 "submissionMediaType" [submissionMediaType] 關鍵字描述目標資源所實作的處理函數的領域。否則,如上所述,對於不相關的操作,提交 schema 和媒體類型將被忽略。
如果存在此屬性,則表示用戶端應用程式和使用者代理程式應使用 "submissionSchema" [submissionSchema] 所描述的請求酬載之媒體類型格式。
省略此關鍵字的行為與 application/json 的值相同。
請注意,"submissionMediaType" 和 "submissionSchema" 不限於 HTTP URI。 [CREF4]此聲明可能會移動到範例結束的地方。
此屬性包含一個 schema,它定義了根據 "submissionMediaType" 屬性編碼並傳送至目標資源以進行處理的文件之可接受結構。這可以被視為描述目標資源所實作的處理函數之領域。
這與 "targetSchema" [targetSchema] 屬性是不同的概念,後者描述目標資訊資源(包括在 PUT 請求中替換資源的內容),而 "submissionSchema" 描述使用者提交的請求資料,以供資源評估。"submissionSchema" 旨在用於具有不一定根據目標表示定義的酬載之請求。
省略 "submissionSchema" 的行為與值為 "true" 的行為相同。
在高層次上,一個符合規範的實作將滿足以下需求。每個需求在個別的關鍵字章節和關鍵字群組概述中都有更詳細的說明。
請注意,關於實作如何必須識別 "self"、"collection" 和 "item" 連結的需求,已在 連結關係類型 [relationType] 章節中詳細說明,此處不再重複。
雖然這不是實作的強制格式,但測試套件中使用的輸出格式總結了每個連結在使用前需要計算的內容。
產生測試套件輸出時,其他不涉及產生上述資訊的 LDO 關鍵字會完全按照其顯示的方式包含在內。除非有特別相關,否則這裡不會進一步討論這些欄位。
在使用連結之前,必須透過將 hyper-schema 應用於實例並找到所有適用且有效的連結來探索它們。請注意,除了收集有效的連結之外,任何解析每個 LDO 的 URI 範本所需的 "base" [base] 值也必須被找到,並透過對實作 URI 範本解析程序最有用的任何機制與 LDO 相關聯。
實作必須支援透過其附加指標或內容指標查找連結,方法是執行查找或提供一組已確定兩個指標的所有連結,以便使用者代理程式可以自行實作查找。
當透過內容指標執行查找時,附加到同一陣列元素的連結必須按照附加的陣列元素的相同順序返回。
三個 hyper-schema 關鍵字是 URI 範本 [RFC6570]:"base"、"anchor" 和 "href"。每個關鍵字都單獨解析為 URI 參考,然後將 anchor 或 href URI 參考針對 base 進行解析(base 本身會根據需要針對較早的 base 進行解析,每個 base 都首先從 URI 範本解析為 URI 參考)。
所有三個關鍵字都共享相同的演算法,用於從實例資料解析變數,該演算法使用 "templatePointers" 和 "templateRequired" 關鍵字。當解析 "href" 時,它和解析為絕對 URI 所需的任何 "base" 範本,該演算法會被修改為選擇性地根據 "hrefSchema" 關鍵字接受使用者輸入。
對於每個 URI 範本 (T),以下虛擬程式碼描述將 T 解析為 URI 參考 (R) 的演算法。為了這個演算法的目的
此演算法應首先應用於 "href" 或 "anchor",然後根據需要應用於每個連續的 "base"。順序很重要,因為並非總是能夠判斷範本將解析為完整 URI 還是 URI 參考。
以英文來說,高層次演算法是
這是作為虛擬程式碼的高層次演算法。"T" 來自 LDO 內的 "href" 或 "anchor",或來自包含 schema 中的 "base"。每個步驟的虛擬程式碼如下。"initialTemplateKeyword" 表示哪個關鍵字啟動了該過程(因為 "base" 始終按順序解析以完成解析其中一個或另一個關鍵字)。
templateData = populateDataFromInstance(T, ldo, instance) if initialTemplateKeyword == "href" and ldo.hrefSchema exists: inputData = acceptInput(ldo, instance, templateData) for varname in inputData: templateData[varname] = inputData[varname] for varname in ldo.templateRequired: if not exists templateData[varname] fatal("Missing required variable(s)") templateData = stringEncode(templateData) R = rfc6570ResolutionAlgorithm(T, templateData)
此步驟會查看實例中的不同位置以查找變數值。對於每個變數
for varname in T: varname = rfc3986PercentDecode(varname) if varname in ldo.templatePointers: valuePointer = templatePointers[varname] if valuePointer is relative: valuePointer = resolveRelative(attachmentPointer, valuePointer) else valuePointer = attachmentPointer + "/" + varname value = instance.valueAt(valuePointer) if value is defined: templateData[varname] = value
由於需要支援多種情況,因此此步驟相對複雜。某些變數會禁止輸入,而某些變數則允許輸入。有些變數會有需要在輸入介面中呈現的初始值,而有些則不會。
"InputForm" 表示任何適當的輸入機制。這可能是一個文字 Web 表單,或者可能是一個更程式化的建構,例如一個接受特定欄位和資料類型(具有給定的初始值,如果有的話)的回呼函數。
form = new InputForm() for varname in T: useField = true useInitialData = true for schema in getApplicableSchemas(ldo.hrefSchema, "/" + varname): if schema is false: useField = false break if varname in templateData and not isValid(templateData[varname], schema)): useInitialData = false break if useField: if useInitialData: form.addInputFieldFor(varname, ldo.hrefSchema, templateData[varname]) else: form.addInputFieldFor(varname, ldo.hrefSchema) inputData = form.acceptInput() if not isValid(inputData, hrefSchema): fatal("Input invalid, link is not usable") return inputData:
本節很簡單,將字面量轉換為其名稱作為字串,並以最明顯的方式將數字轉換為字串,並根據需要在 URI 中使用百分比編碼。
for varname in templateData: value = templateData[varname] if value is true: templateData[varname] = "true" else if value is false: temlateData[varname] = "false" else if value is null: templateData[varname] = "null" else if value is a number: templateData[varname] = bestEffortOriginalJsonString(value) else: templateData[varname] = rfc3986PercentEncode(value)
在某些軟體環境中,可能無法使用數字的原始 JSON 表示 (無法區分 1.0 和 1),因此應使用任何合理的表示。Schema 和 API 作者應牢記這一點,如果確切的表示很重要,則應使用其他類型(例如字串或布林值)。如果數字是以字串形式作為輸入提供,則應使用作為輸入使用的字串。
對於給定的連結,實作必須讓所有目標屬性關鍵字的值直接提供給使用者代理程式。實作可以提供額外的介面來使用這些資訊,如每個關鍵字的章節中所討論的。
對於給定的連結,實作必須讓每個輸入架構關鍵字的值直接提供給使用者代理程式。
為了鼓勵 URI 範本解析過程的封裝,實作可以省略僅用於建構 URI 的 LDO 關鍵字。然而,實作必須提供連結關係類型的存取權。
無法識別的關鍵字應提供給使用者代理程式,否則必須忽略。
超架構實作應提供建構任何有效目標資源請求所需的所有資訊的存取權。
LDO 可以表達對連結執行任何操作所需的所有資訊。本節說明使用者代理程式應檢查哪些超架構欄位,以從任何實例資料和用戶端輸入的組合中建立請求。超架構實作本身不應建構和傳送請求。
目標 URI 建構規則,包括用於接收輸入的 "hrefSchema",對於所有可能的請求都是相同的。
不攜帶主體酬載的請求不需要額外的關鍵字支援。
將目標表示形式作為酬載的請求應使用 "targetSchema" 和 "targetMediaType" 關鍵字進行輸入描述和酬載驗證。如果協定允許操作採用基於表示形式並由媒體類型修改的酬載(例如修補媒體類型),則應以協定特定的方式透過 "targetHints" 指示此類媒體類型。
採用非衍生自目標資源表示形式的酬載的請求應使用 "submissionSchema" 和 "submissionMediaType" 關鍵字進行輸入描述和酬載驗證。超媒體中使用的協定通常每個連結僅支援一個此類非表示形式操作。
透過單個超媒體協定操作傳遞許多具有任意不同請求結構的應用程式操作的 RPC 系統,超出 JSON 超架構等超媒體格式的範圍。
作為超媒體格式,JSON 超架構關注描述資源,包括以足夠的細節描述其連結,以提出所有有效的請求。它不關注直接描述這些請求的所有可能的回應。
如同任何超媒體系統一樣,回應預期是自我描述的。在超架構的背景下,這表示每個回應必須連結其自身的超架構。雖然預期由目標資源表示形式組成的回應會根據 "targetSchema" 和 "targetMediaType" 有效,但這些關鍵字僅供參考,如果與回應本身衝突,則必須忽略。
其他回應,包括錯誤回應、複雜的重新導向和處理狀態表示形式,也應連結到其自身的架構並使用適當的媒體類型(例如,"application/problem+json" [RFC7807] 用於錯誤)。某些錯誤可能由於是由不了解超架構的中介產生,而不是由來源產生,因此可能不會連結架構。
使用者代理程式預期能充分理解協定狀態碼和回應媒體類型,以處理常見情況,並提供足夠的資訊給用戶端應用程式來處理特定領域的回應。
在設計時靜態地對應所有可能的回應及其架構超出 JSON 超架構的範圍,但可能在其他基於超架構建構的 JSON 架構詞彙的範圍內(請參閱 附錄 A.3)。
根據連結的上下文發現連結,或使用連結的上下文識別集合的需求,在使用串流剖析器時會產生獨特的挑戰。在不處理整個架構和實例文件的情況下,無法權威地滿足這些需求。
此類實作可以選擇根據迄今處理的資料傳回非權威的答案。在提供此方法時,實作必須清楚說明回應的性質,並且必須提供一個選項來封鎖並等待,直到處理完所有資料並且可以傳回權威的答案。
雖然 JSON 超架構是一種超媒體格式,因此與協定無關,但預期其最常見的用途將是在 HTTP 系統中,或使用明確類似 HTTP 的協定(如 CoAP)的系統中。
本節提供有關如何將每個常見的 HTTP 方法與連結一起使用的指南,以及集合資源如何對 HTTP POST 施加額外的約束。此外,還提供了有關提示 HTTP 回應標頭值以及描述與給定資源相關的可能 HTTP 請求標頭的指南。
JSON 架構核心規格的第 11 節 [json-schema] 提供了有關將超媒體系統中的實例連結到其架構的指南。這可以使用可網路存取的架構來完成,或僅識別預先封裝在用戶端應用程式中的架構。JSON 超架構有意不限制此機制,儘管建議盡可能使用核心規格中概述的技術。
連結描述物件不直接指示目標資源支援哪些操作(例如 HTTP 方法)。相反地,操作應主要從連結關係類型 [rel] 和 URI 方案推斷出來。
這表示對於每個目標資源和連結關係類型配對,架構作者應僅定義單個 LDO。雖然可以使用 "allow" 和 "targetHints" 重複具有不同允許 HTTP 方法的關係類型和目標配對,但不建議這樣做,且符合規範的實作可能不完全支援。
使用每個 HTTP 方法所需的所有資訊都可以在單個 LDO 中傳達,如本節所述。"targetHints" 中的 "allow" 欄位僅用於提示支援哪些操作,而不是單獨定義每個操作。
但是請注意,資源始終可以在執行時拒絕操作,例如由於授權失敗,或由於控制操作可用性的其他應用程式狀態。
"targetSchema" 描述連結目標端的資源,而 "targetMediaType" 定義該資源的媒體類型。對於 HTTP 連結,"headerSchema" 也可用於描述 "Accept" 請求標頭中使用的有效值,該標頭可以支援多種媒體類型或媒體範圍。當同時存在兩種指示目標媒體類型的方法時,"targetMediaType" 應指示預設表示媒體類型,而 "headerSchema" 中 "accept" 的架構應包括預設值以及任何可以請求的替代媒體類型或媒體範圍。
由於許多 HTTP 方法的語義是根據目標資源定義的,因此 "targetSchema" 用於多種 HTTP 方法的請求和/或回應。特別地,"targetSchema" 建議用戶端應用程式可以預期 HTTP GET 的回應,或者任何 "Content-Location" 標頭等於請求 URI 的回應,以及如果用戶端應用程式在 HTTP PUT 請求中替換資源時應傳送的內容。這些關聯由 RFC 7231, section 4.3.1 - "GET", section 4.3.4 "PUT", and section 3.1.4.2, "Content-Location" [RFC7231] 定義。
根據 RFC 5789 [RFC5789],HTTP PATCH 的請求結構取決於 "targetSchema" 和請求媒體類型的組合,請求媒體類型由 "Accept-Patch" 標頭傳達,該標頭可能包含在 "targetHints" 中。適用於 PATCH 的媒體類型定義了表達文件變更的語法,該語法可以應用於 "targetSchema" 描述的表示形式,以確定語法有效的請求酬載集。通常,驗證 PATCH 請求的最簡單方法是應用它並將結果驗證為正常表示形式。
JSON 超架構允許資源除了處理目標的表示形式之外,或替代處理目標的表示形式,還可以處理任意資料。此任意資料由 "submissionSchema" 和 "submissionMediaType" 關鍵字描述。在 HTTP 的情況下,POST 方法是唯一處理此類資料的方法。雖然有一些關於將 POST 與集合一起使用的約定,但 POST 請求的語義由目標資源定義,而不是 HTTP 定義。
除了與協定無關的 "submission*" 關鍵字(有關非 HTTP 範例,請參閱第 9.3 節)之外,"Accept-Post" 標頭可用於指定必要的媒體類型,並且可以透過 "targetHints" 欄位進行宣告。[CREF5]如果兩者都使用會發生什麼事?此外,必須支援 "submissionSchema",而 "targetHints" 最多為 SHOULD。但是禁止在 "targetHints" 中使用 "Accept-Post" 似乎不正確。
POST 的成功回應(除了設定 "Content-Location" 的 201 或 200 之外)同樣沒有 HTTP 定義的語義。與所有 HTTP 回應一樣,回應中的任何表示形式都應連結到其自身的超架構,以指示如何處理。如附錄 A.2中所述,將超連結與所有可能的操作回應連接起來不在 JSON 超架構的範圍內。
HTTP 回應標頭資訊的 JSON 序列化應遵循正在進行的 "HTTP 標頭欄位值的 JSON 編碼" [I-D.reschke-http-jfv] 中建立的指南。該文件中範例中顯示的方法應盡可能應用於其他類似結構的標頭。
所有可能的 HTTP 方法回應的標頭都共用 "headerSchema"。特別是,HEAD 回應中出現的標頭和 OPTIONS 回應中出現的標頭都可以出現。"headerSchema" 中未區分哪個方法回應包含哪個標頭。
建議架構作者在適用時提供以下類型 HTTP 標頭值的提示
一般而言,在不同時間可能會有不同值的標頭,不應包含在「targetHints」中。
Schema 應以描述 JSON 序列化的方式撰寫,這些序列化遵循正在進行中的 「HTTP 標頭欄位值的 JSON 編碼」 [I-D.reschke-http-jfv] 中建立的準則。該文件中範例中顯示的方法應盡可能應用於其他類似結構的標頭。
建議 schema 作者在適用的情況下描述以下類型 HTTP 標頭的可用用法:
諸如快取控制和條件式請求標頭之類的標頭,通常是由中介機構而非資源實作,因此通常不適合描述。雖然資源必須提供使用條件式請求所需的信息,但此類標頭和相關回應的運行時處理並非資源特定的。
當使用 HTTP 或與 HTTP 明確類似的協定(例如 CoAP)時,這是透過將要建立的個別資源的表示法 POST 到集合資源來完成的。辨識集合和項目資源的過程在第 6.2.3 節中說明。
JSON 超級 Schema 有助於 HTTP 內容協商,並允許主動和被動策略的混合。如上所述,超級 schema 可以使用「headerSchema」關鍵字,包含用於 HTTP 標頭(例如「Accept」、「Accept-Charset」、「Accept-Language」等)的 schema。使用者代理程式或用戶端應用程式可以使用此 schema 中的資訊(例如支援的語言列舉列表),而無需發出初始請求來啟動被動協商過程。
透過這種方式,設定這些標頭的主動內容協商技術可以從伺服器資訊中得知哪些值是可能的,類似於檢查被動協商中的替代方案清單。
對於允許將 schema 指定為媒體類型參數的媒體類型,「Accept」在請求中傳送或在「headerSchema」中宣告的值,可以包含預期協商的表示法符合的 schema 的 URI。內容協商中使用 schema 參數的一種可能用途是,如果資源隨著時間的推移符合多個不同的 schema 版本。用戶端應用程式可以透過這種方式在「Accept」標頭中指出它理解哪個或哪些版本。
本節顯示如何使用建構 URI 和 JSON 指標的關鍵字。結果會以測試套件使用的格式顯示。 [CREF7]需要發布並連結它,但對於現階段審閱的人來說應該是不言自明的。
大多數其他關鍵字都是簡單明瞭的(「title」和「description」),將驗證應用於特定種類的輸入、請求或回應,或者具有特定協定的行為。示範 HTTP 用法的範例可在附錄中取得 [HTTP]。
在此範例中,我們將假設一個範例 API,其記錄的進入點 URI 為 https://example.com,這是一個空的 JSON 物件,其中包含指向 schema 的連結。在此,進入點本身沒有任何資料,僅存在於提供一組初始連結。
GET https://api.example.com HTTP/1.1 200 OK Content-Type: application/json Link: <https://schema.example.com/entry> rel=describedBy {}
連結的超級 schema 定義 API 的基本 URI,並提供兩個連結:「about」連結,指向 API 文件;以及「self」連結,表示這是基本 URI 的 schema。在這種情況下,基本 URI 也是進入點 URI。
{ "$id": "https://schema.example.com/entry", "$schema": "https://json-schema.dev.org.tw/draft-07-wip/hyper-schema#", "base": "https://api.example.com", "links": [ { "rel": "self", "href": "" }, { "rel": "about", "href": "/docs" } ] }
這些是最簡單的連結,只有關聯類型和「href」,沒有範本變數。它們解析如下:
[ { "contextUri": "https://api.example.com", "contextPointer": "", "rel": "self", "targetUri": "https://api.example.com", "attachmentPointer": "" }, { "contextUri": "https://api.example.com", "contextPointer": "", "rel": "about", "targetUri": "https://api.example.com/docs", "attachmentPointer": "" } ]
附加指標是根指標(實例為空物件時的唯一可能性)。內容 URI 是預設值,即請求的文件。由於 application/json 不允許片段,因此內容指標是完整描述內容所必需的。它的預設行為與附加指標相同。
讓我們在系統中新增「東西」,從個別的東西開始。
{ "$id": "https://schema.example.com/thing", "$schema": "https://json-schema.dev.org.tw/draft-07-wip/hyper-schema#", "base": "https://api.example.com", "type": "object", "required": ["data"], "properties": { "id": {"$ref": "#/definitions/id"}, "data": true }, "links": [ { "rel": "self", "href": "things/{id}", "templateRequired": ["id"], "targetSchema": {"$ref": "#"} } ], "definitions": { "id": { "type": "integer", "minimum": 1, "readOnly": true } } }
我們的「東西」具有伺服器指派的 id,這是建構「self」連結所必需的。它還有一個「data」欄位,可以是任何類型。「definitions」區段的原因將在下一個範例中說明。
請注意,「id」並非驗證 schema 所必需的,而是自我連結所必需的。這是合理的:只有在建立「東西」並且伺服器指派了 id 時,「東西」才具有 URI。但是,您可以將此 schema 與僅包含資料欄位的實例搭配使用,這可讓您驗證您即將建立的「東西」實例。
讓我們在進入點 schema 中新增一個連結,如果您可以將其 id 作為輸入,就可以直接跳轉到特定的東西。為了節省空間,只顯示新的 LDO。與「self」和「about」不同,沒有關於假設東西的 IANA 註冊關係,因此使用 「tag:」URI 方案 [RFC4151] 定義擴充關係。
{ "rel": "tag:rel.example.com,2017:thing", "href": "things/{id}", "hrefSchema": { "required": ["id"], "properties": { "id": {"$ref": "thing#/definitions/id"} } }, "targetSchema": {"$ref": "thing#"} }
這裡的「href」值相同,但其他所有內容都不同。回想一下,實例是一個空物件,因此無法從實例資料解析「id」。相反,它需要作為客戶端輸入。此 LDO 也可以使用「templateRequired」,但在「hrefSchema」中使用「required」並非嚴格必要。在「hrefSchema」中未將「id」標示為必要的狀況下提供「templateRequired」將會導致錯誤,因為客戶端輸入是解析此連結的唯一可能來源。
此範例涵蓋使用「submission」欄位來處理非表示法輸入,以及將它們與使用輸入解析 URI 範本一起使用。與 HTML 表單不同,HTML 表單需要建構 URI 或傳送有效負載,但不允許兩者同時進行,JSON 超級 Schema 可以描述同一連結上相同操作的兩種輸入。
「submissionSchema」和「submissionMediaType」欄位用於描述非目標資源表示法的有效負載。當與「http(s)://」URI 一起使用時,它們通常是指 POST 請求有效負載,如關於 HTTP 用法的附錄中所示 [HTTP]。
在此情況下,我們使用「mailto:」URI,根據RFC 6068 第 3 節 [RFC6068],它不提供任何用於檢索資源的操作。它只能用於建構要傳送的訊息。由於沒有可檢索、可替換或可刪除的目標資源的概念,因此不使用「targetSchema」和「targetMediaType」。非表示法有效負載由「submissionSchema」和「submissionMediaType」描述。
我們使用「submissionMediaType」來指示 multipart/alternative 有效負載格式,提供相同資料的兩種表示法(HTML 和純文字)。由於 multipart/alternative 訊息是有序序列(最後一個部分是最偏好的替代方案),因此我們在「submissionSchema」中將序列建模為陣列。由於每個部分本身都是具有媒體類型的文件,因此我們將陣列中的每個項目建模為字串,使用「contentMediaType」來指示字串內的格式。
請注意,應將諸如 multipart/form-data 之類的媒體類型(將名稱與每個部分關聯且不排序)建模為 JSON 物件而不是陣列。
請注意,某些行被換行以符合本文檔的寬度限制。
{ "$id": "https://schema.example.com/interesting-stuff", "$schema": "https://json-schema.dev.org.tw/draft-07-wip/hyper-schema#", "required": ["stuffWorthEmailingAbout", "email", "title"], "properties": { "title": { "type": "string" }, "stuffWorthEmailingAbout": { "type": "string" }, "email": { "type": "string", "format": "email" }, "cc": false }, "links": [ { "rel": "author", "href": "mailto:{email}?subject={title}{&cc}", "templateRequired": ["email"], "hrefSchema": { "required": ["title"], "properties": { "title": { "type": "string" }, "cc": { "type": "string", "format": "email" }, "email": false } }, "submissionMediaType": "multipart/alternative; boundary=ab2", "submissionSchema": { "type": "array", "items": [ { "type": "string", "contentMediaType": "text/plain; charset=utf8" }, { "type": "string", "contentMediaType": "text/html" } ], "minItems": 2 } } ] }
對於 URI 參數,三個參數中的每一個都示範了不同的解析輸入方法:
因此,假設從「https://api.example.com/stuff」檢索以下實例:
{ "title": "The Awesome Thing", "stuffWorthEmailingAbout": "Lots of text here...", "email": "[email protected]" }
我們可以在要求用戶端應用程式輸入之前,部分解析連結如下:
{ "contextUri": "https://api.example.com/stuff", "contextPointer": "", "rel": "author", "hrefInputTemplates": [ "mailto:[email protected]?subject={title}{&cc}" ], "hrefPrepopulatedInput": { "title": "The Really Awesome Thing" }, "attachmentPointer": "" }
請注意,使用「targetUri」的「href*」關鍵字。這些是三種可能的「targetUri」值,涵蓋不同的輸入類型。以下是每個值的範例:
連結是從內容資源到目標資源的類型化連線。較舊的連結序列化支援「rev」關鍵字,該關鍵字採用連結關聯類型,如同「rel」一樣,但會反轉語意。這已經被棄用很長時間了,因此 JSON 超級 Schema 不支援它。相反,「anchor」的變更內容 URI 的能力可用於反轉連結的方向。它也可以用於描述兩個資源之間的連結,這兩個資源都不是目前的資源。
例如,有一個 IANA 註冊的「up」關係,但沒有「down」。在 HTTP Link 標頭中,您可以將「down」實作為 「rev」:「up」。
首先,讓我們看看如何在 HTTP 中完成此操作,顯示「self」連結和兩個語意相同的連結,一個使用「rev」:「up」,另一個使用「anchor」與「rel」:「up」(由於格式限制,行已換行)。
GET https://api.example.com/trees/1/nodes/123 HTTP/1.1 200 OK Content-Type: application/json Link: <https://api.example.com/trees/1/nodes/123> rel=self Link: <https://api.example.com/trees/1/nodes/123> rel=up anchor=<https://api.example.com/trees/1/nodes/456> Link: <https://api.example.com/trees/1/nodes/456> rev=up { "id": 123, "treeId": 1, "childIds": [456] }
請注意,「rel=up」連結的目標 URI 與「rel=self」連結相同,並將「anchor」(識別連結的內容)設定為子系的 URI。當同時存在「self」連結時,工具可以輕鬆偵測到這種反轉連結。
以下超級 schema,應用於上述回應中的實例,將產生相同的「self」連結和使用「anchor」的「up」連結。它還顯示了範本化「base」URI 的使用,以及「templatePointers」中的絕對和相對 JSON 指標。
"base": "trees/{treeId}", "properties": { "id": {"type": "integer"}, "treeId": {"type": "integer"}, "childIds": { "type": "array", "items": { "type": "integer", "links": [ { "anchor": "nodes/{thisNodeId}", "rel": "up", "href": "nodes/{childId}", "templatePointers": { "thisNodeId": "/id", "childId": "0" } } ] } } }, "links": [ { "rel": "self", "href": "nodes/{id}" } ] }
對於目標 URI(「href」)和上下文 URI(「anchor」),「base」模板的評估方式完全相同。
請注意使用的兩種不同模板指標類型。「thisNodeId」會映射到一個絕對 JSON 指標「/id」,而「childId」會映射到一個相對指標「0」,表示目前項目的值。絕對 JSON 指標不支援任何形式的萬用字元,因此如果沒有相對 JSON 指標,就無法指定像「目前項目」這樣的概念。
在許多系統中,個別資源會被分組到集合中。這些集合通常也提供一種方法,可以用伺服器指定的識別碼來建立個別的項目資源。
在此範例中,我們將重複使用先前章節中顯示的個別事物結構描述。為了方便起見,這裡重複顯示,並新增了一個「collection」連結,其「targetSchema」參考指向我們接下來要介紹的集合結構描述。
{ "$id": "https://schema.example.com/thing", "$schema": "https://json-schema.dev.org.tw/draft-07-wip/hyper-schema#", "base": "https://api.example.com", "type": "object", "required": ["data"], "properties": { "id": {"$ref": "#/definitions/id"}, "data": true }, "links": [ { "rel": "self", "href": "things/{id}", "templateRequired": ["id"], "targetSchema": {"$ref": "#"} }, { "rel": "collection", "href": "/things", "targetSchema": {"$ref": "thing-collection#"}, "submissionSchema": {"$ref": "#"} } ], "definitions": { "id": { "type": "integer", "minimum": 1, "readOnly": true } } }
「collection」連結對於所有項目都相同,因此沒有 URI 模板變數。「submissionSchema」是項目本身的結構描述。如第 6.2.3 節所述,如果「collection」連結支援提交機制(HTTP 中的 POST),則它必須實作項目建立語意。因此,「submissionSchema」是透過此連結建立「事物」的結構描述。
現在我們要描述「事物」的集合。此結構描述描述一個集合,其中每個項目表示法都與個別「事物」表示法相同。雖然許多集合表示法僅包含項目表示法的子集,但此範例使用全部表示法,以盡量減少涉及的結構描述數量。實際的集合項目會以物件中的陣列形式出現,因為我們將在下一個範例中向該物件新增更多欄位。
{ "$id": "https://schema.example.com/thing-collection", "$schema": "https://json-schema.dev.org.tw/draft-07-wip/hyper-schema#", "base": "https://api.example.com", "type": "object", "required": ["elements"], "properties": { "elements": { "type": "array", "items": { "allOf": [{"$ref": "thing#"}], "links": [ { "anchorPointer": "", "rel": "item", "href": "things/{id}", "templateRequired": ["id"], "targetSchema": {"$ref": "thing#"} } ] } } }, "links": [ { "rel": "self", "href": "things", "targetSchema": {"$ref": "#"}, "submissionSchema": {"$ref": "thing"} } ] }
{ "elements": [ {"id": 12345, "data": {}}, {"id": 67890, "data": {}} ] }
以下是適用於此實例的所有連結,包括在參考的個別「事物」結構描述中定義的連結
[ { "contextUri": "https://api.example.com/things", "contextPointer": "", "rel": "self", "targetUri": "https://api.example.com/things", "attachmentPointer": "" }, { "contextUri": "https://api.example.com/things", "contextPointer": "/elements/0", "rel": "self", "targetUri": "https://api.example.com/things/12345", "attachmentPointer": "/elements/0" }, { "contextUri": "https://api.example.com/things", "contextPointer": "/elements/1", "rel": "self", "targetUri": "https://api.example.com/things/67890", "attachmentPointer": "/elements/1" }, { "contextUri": "https://api.example.com/things", "contextPointer": "", "rel": "item", "targetUri": "https://api.example.com/things/12345", "attachmentPointer": "/elements/0" }, { "contextUri": "https://api.example.com/things", "contextPointer": "", "rel": "item", "targetUri": "https://api.example.com/things/67890", "attachmentPointer": "/elements/1" }, { "contextUri": "https://api.example.com/things", "contextPointer": "/elements/0", "rel": "collection", "targetUri": "https://api.example.com/things", "attachmentPointer": "/elements/0" }, { "contextUri": "https://api.example.com/things", "contextPointer": "/elements/1", "rel": "collection", "targetUri": "https://api.example.com/things", "attachmentPointer": "/elements/1" } ]
在所有情況下,上下文 URI 都會顯示為媒體類型 application/json 的實例,該類型不支援片段。如果實例媒體類型為 application/instance+json(支援 JSON 指標片段),則上下文 URI 將包含與上下文指標欄位相同的片段。對於 application/json 和其他不含片段的媒體類型,務必同時考慮上下文指標和上下文 URI。
有三個「self」連結,一個用於集合,另一個用於「elements」陣列中的每個項目。項目「self」連結在個別「事物」結構描述中定義,並透過「$ref」參考。這三個連結可以透過其上下文或附加指標來區分。我們將在第 9.5.2 節中重新探討集合「self」連結的「submissionSchema」。
有兩個「item」連結,一個用於「elements」陣列中的每個項目。與「self」連結不同,這些連結僅在集合結構描述中定義。它們每個都與共享相同附加指標的相應「self」連結具有相同的目標 URI。但是,它們每個都有不同的上下文指標。「self」連結的上下文是「elements」中的項目,而「item」連結的上下文始終是整個集合,無論具體的項目為何。
最後,還有兩個「collection」連結,一個用於「elements」中的每個項目。在個別項目結構描述中,這些連結會產生以項目資源為上下文的連結。從集合結構描述參考時,上下文是「elements」陣列中相關「事物」的位置,而不是該「事物」本身的獨立資源 URI。
集合連結具有相同的目標 URI,因為只有一個相關的集合 URI。雖然將這兩個連結計算為完整建構連結集的一部分可能看起來沒有用,但在按需建構連結時,這種安排意味著無論您處理哪個「elements」項目,都隨時可以取得「collection」連結定義。
在這裡,我們為集合新增分頁。「meta」部分用於保存有關目前頁面、下一頁和上一頁的資訊。大部分結構描述與上一節相同,因此已省略。僅完整顯示新欄位和新的或(針對主要「self」連結)已變更的連結。
{ "properties": { "elements": { ... }, "meta": { "type": "object", "properties": { "prev": {"$ref": "#/definitions/pagination"}, "current": {"$ref": "#/definitions/pagination"}, "next": {"$ref": "#/definitions/pagination"} } } }, "links": [ { "rel": "self", "href": "things{?offset,limit}", "templateRequired": ["offset", "limit"], "templatePointers": { "offset": "/meta/current/offset", "limit": "/meta/current/limit" }, "targetSchema": {"$ref": "#"} }, { "rel": "prev", "href": "things{?offset,limit}", "templateRequired": ["offset", "limit"], "templatePointers": { "offset": "/meta/prev/offset", "limit": "/meta/prev/limit" }, "targetSchema": {"$ref": "#"} }, { "rel": "next", "href": "things{?offset,limit}", "templateRequired": ["offset", "limit"], "templatePointers": { "offset": "/meta/next/offset", "limit": "/meta/next/limit" }, "targetSchema": {"$ref": "#"} } ], "definitions": { "pagination": { "type": "object", "properties": { "offset": { "type": "integer", "minimum": 0, "default": 0 }, "limit": { "type": "integer", "minimum": 1, "maximum": 100, "default": 10 } } } } }
請注意,「self」連結包含產生確切表示法的分頁查詢,而不是允許透過輸入選擇頁面的集合通用連結。
給定此實例
{ "elements": [ {"id": 12345, "data": {}}, {"id": 67890, "data": {}} ], "meta": { "current": { "offset": 0, "limit": 2 }, "next": { "offset": 3, "limit": 2 } } }
以下是適用於此實例的所有連結,這些連結在先前的範例中沒有出現,或已透過新增分頁進行變更。
[ { "contextUri": "https://api.example.com/things", "contextPointer": "", "rel": "self", "targetUri": "https://api.example.com/things?offset=20,limit=2", "attachmentPointer": "" }, { "contextUri": "https://api.example.com/things", "contextPointer": "", "rel": "next", "targetUri": "https://api.example.com/things?offset=22,limit=2", "attachmentPointer": "" } ]
請注意,輸出中沒有「prev」連結,因為我們正在查看第一頁。「meta」下缺少「prev」欄位,以及「prev」連結的「templateRequired」值,表示該連結無法用於此特定實例。
讓我們為此集合新增到進入點結構描述的連結(第 9.1 節),包括分頁輸入,以便用戶端應用程式可以直接跳到特定頁面。回想一下,進入點結構描述僅包含連結,因此我們僅顯示新新增的連結
{ "rel": "tag:rel.example.com,2017:thing-collection", "href": "/things{?offset,limit}", "hrefSchema": { "$ref": "thing-collection#/definitions/pagination" }, "submissionSchema": { "$ref": "thing#" }, "targetSchema": { "$ref": "thing-collection#" } }
現在我們看到分頁參數被接受為輸入,因此我們可以跳到集合中的任何頁面。連結關係類型是自訂的,因為通用「collection」連結只能在項目作為上下文時使用,而不能在進入點或其他資源上使用。
當我們沒有任何「事物」時,我們沒有任何具有相關「collection」連結的資源。因此,我們無法使用「collection」連結的提交關鍵字來建立第一個「事物」;超結構描述必須針對實例進行評估。由於集合實例中的「elements」陣列為空,因此它也無法為我們提供集合連結。
但是,我們的進入點連結可以將我們帶到空的集合,我們可以透過超結構描述中「item」連結的存在來識別它是一個集合。由於「item」連結的上下文是集合,因此我們只需尋找具有相同上下文的「self」連結,然後我們可以將其視為用於建立操作的集合。
想必,進入點結構描述中的自訂連結關係類型足以確保我們找到了正確的集合。識別自訂連結關係類型的用戶端應用程式可能會知道它可以立即假設目標是集合,但通用使用者代理程式無法這樣做。儘管我們的範例中存在「-collection」字尾,但通用使用者代理程式無法知道該子字串是否表示超媒體資源集合或其他類型的集合。
一旦我們將「self」連結識別為正確集合的連結,我們就可以使用其「submissionSchema」和/或「submissionMediaType」關鍵字來執行項目建立操作。[CREF9]如果集合是未篩選且未分頁的,這會非常完美。但是,一般應該將 POST 發送到將包含建立的資源的集合,而「self」連結必須包含任何篩選器、分頁或其他查詢參數。即使產生的項目不符合篩選條件或未出現在該頁面中,將 POST 發送到此類「self」連結仍然有效嗎?請參閱 GitHub 問題 #421 以了解更多討論。 [CREF10]超結構描述的 Draft-04 定義了一個以結構描述(而非實例)為上下文的「create」連結關係。這不符合基於實例的連結模型,並且錯誤地使用了連結關係類型的操作名稱。但是,定義從結構描述到集合實例的更正確設計的連結可能是解決此問題的一種可能方法。同樣,請參閱 GitHub 問題 #421 以了解更多詳細資訊。
JSON 超結構描述為 JSON 結構描述核心定義詞彙,並涉及其中列出的所有安全性考量。作為連結序列化格式,RFC 8288 Web Linking [RFC8288] 的安全性考量也適用,並進行適當調整(例如,「anchor」作為 LDO 關鍵字,而不是 HTTP Link 標頭屬性)。
如第 6.5 節所述,所有描述目標資源的 LDO 關鍵字都是建議性的,並且不得用來取代目標資源在回應操作時提供的授權資訊。目標資源回應應指示其自己的超結構描述,該結構描述是授權的。
如果目標回應中的超結構描述與找到目前 LDO 的超結構描述(依據「$id」)相符,則目標屬性可被視為授權的。[CREF11]需要新增有關透過「$id」進行詐騙的風險的內容,但是鑑於規格的其他部分不鼓勵總是重新下載連結的結構描述,因此風險緩解選項尚不清楚。
使用者代理程式或用戶端應用程式不得使用「targetSchema」的值來協助解譯在遵循連結時收到的回應資料,因為這會使「安全」資料重新解譯。
在選擇如何解譯資料時,必須僅考慮伺服器提供的類型資訊(或從檔案名稱推斷的類型資訊,或任何其他通常方法),並且不得使用連結的「targetMediaType」屬性。使用者代理程式可以使用此資訊來判斷它們如何表示連結或在何處顯示連結(例如,懸停文字、在新索引標籤中開啟)。如果使用者代理程式決定將連結傳遞給外部程式,則它們應首先驗證資料的類型是否通常會傳遞給該外部程式。
這是為了防止重新解譯「安全」資料,類似於「targetSchema」的預防措施。
以「targetHints」傳達的協定中繼資料值不得視為授權。根據對中繼資料值的錯誤假設而可能適用的協定定義的任何安全性考量都適用。
即使沒有直接適用的協定安全性考量,實作也必須準備好處理與連結「targetHints」值不符的回應。
當使用「self」的連結關係來表示物件的完整表示法時,如果目標 URI 並不等於或不是包含帶有「self」連結的目標 URI 的資源表示法的請求所用 URI 的子路徑,使用者代理 **不應** 認為該表示法是目標 URI 所表示資源的權威表示法。[CREF12]關於本段中「子路徑」選項的意圖,目前已不太清楚。其意圖可能是允許集合中嵌入項目表示的「self」連結,這些連結的目標 URI 通常是該集合 URI 的子路徑,並將其視為權威的。然而,這只是一種常見的設計慣例,並且似乎並非基於 RFC 3986 或其他關於 URI 使用的指南。請參閱 GitHub issue #485 以了解更多討論。
感謝 Gary Court、Francis Galiegue、Kris Zyp 和 Geraint Luff 在 JSON Schema 的初始草案中所做的工作。
感謝 Jason Desrosiers、Daniel Perrett、Erik Wilde、Ben Hutton、Evgeny Poberezkin、Brad Bowman、Gowry Sankar、Donald Pipowitch、Dave Finlay 和 Denis Laxalde 對本文檔的提交和修正。
[RFC2119] | Bradner, S., "在 RFC 中用於指示需求等級的關鍵字",BCP 14, RFC 2119, DOI 10.17487/RFC2119, 1997 年 3 月。 |
[RFC3986] | Berners-Lee, T., Fielding, R. 和 L. Masinter, "統一資源識別符 (URI):通用語法",STD 66, RFC 3986, DOI 10.17487/RFC3986, 2005 年 1 月。 |
[RFC4287] | Nottingham, M. 和 R. Sayre, "Atom 聯合格式",RFC 4287, DOI 10.17487/RFC4287, 2005 年 12 月。 |
[RFC6570] | Gregorio, J., Fielding, R., Hadley, M., Nottingham, M. 和 D. Orchard, "URI 模板",RFC 6570, DOI 10.17487/RFC6570, 2012 年 3 月。 |
[RFC6573] | Amundsen, M., "項目和集合連結關係",RFC 6573, DOI 10.17487/RFC6573, 2012 年 4 月。 |
[RFC6901] | Bryan, P., Zyp, K. 和 M. Nottingham, "JavaScript 物件表示法 (JSON) 指標",RFC 6901, DOI 10.17487/RFC6901, 2013 年 4 月。 |
[RFC8288] | Nottingham, M., "Web 連結",RFC 8288, DOI 10.17487/RFC8288, 2017 年 10 月。 |
[relative-json-pointer] | Luff, G. 和 H. Andrews, "相對 JSON 指標",Internet-Draft draft-handrews-relative-json-pointer-00, 2017 年 11 月。 |
[json-schema] | Wright, A. 和 H. Andrews, "JSON Schema:用於描述 JSON 文件的一種媒體類型",Internet-Draft draft-handrews-json-schema-00, 2017 年 11 月。 |
[json-schema-validation] | Wright, A., Andrews, H. 和 G. Luff, "JSON Schema 驗證:用於 JSON 結構驗證的詞彙表",Internet-Draft draft-handrews-json-schema-validation-00, 2017 年 11 月。 |
[RFC2046] | Freed, N. 和 N. Borenstein, "多用途網際網路郵件擴展 (MIME) 第二部分:媒體類型",RFC 2046, DOI 10.17487/RFC2046, 1996 年 11 月。 |
[RFC4151] | Kindberg, T. 和 S. Hawke, "「tag」URI 方案",RFC 4151, DOI 10.17487/RFC4151, 2005 年 10 月。 |
[RFC5789] | Dusseault, L. 和 J. Snell, "HTTP 的 PATCH 方法",RFC 5789, DOI 10.17487/RFC5789, 2010 年 3 月。 |
[RFC6068] | Duerst, M., Masinter, L. 和 J. Zawinski, "「mailto」URI 方案",RFC 6068, DOI 10.17487/RFC6068, 2010 年 10 月。 |
[RFC7230] | Fielding, R. 和 J. Reschke, "超文本傳輸協定 (HTTP/1.1):訊息語法和路由",RFC 7230, DOI 10.17487/RFC7230, 2014 年 6 月。 |
[RFC7231] | Fielding, R. 和 J. Reschke, "超文本傳輸協定 (HTTP/1.1):語義和內容",RFC 7231, DOI 10.17487/RFC7231, 2014 年 6 月。 |
[RFC7807] | Nottingham, M. 和 E. Wilde, "HTTP API 的問題詳細資訊",RFC 7807, DOI 10.17487/RFC7807, 2016 年 3 月。 |
[I-D.reschke-http-jfv] | Reschke, J., "用於 HTTP 標頭欄位值的 JSON 編碼",Internet-Draft draft-reschke-http-jfv-06, 2017 年 6 月。 |
遵循 REST 架構風格約束的超媒體 API 能夠建立通用的使用者代理。這種使用者代理不具有特定於應用程式的知識。相反地,它通過識別這些內容並協調使用現有軟體來實現對它們的支持,來理解預定義的媒體類型、URI 方案、協定和連結關係。然後,可以在此使用者代理之上建立客戶端應用程式,使其專注於它們自己的語義和邏輯,而不是互動的機制。
Hyper-schema 一次只關注一個資源和一組相關的連結。就像網頁瀏覽器一次只處理一個 HTML 頁面,而沒有概念這個頁面是否或如何作為「網站」的一部分運作一樣,一個了解 hyper-schema 的使用者代理一次只處理一個資源,而沒有任何關於該資源是否或如何適合 API 的概念。
因此,hyper-schema 適用於在 API 中使用,但不適用於將 API 描述為完整的實體。沒有辦法描述 API 範圍的概念,而不是資源和連結範圍的概念,這種描述超出了 JSON Hyper-Schema 的界限。
由於給定的 JSON Hyper-Schema 在單一時間點與單一資源一起使用,它沒有固有的版本控制概念。然而,給定的資源可以隨著時間的推移改變它使用的 schema 或 schemas,而這些 schema 的 URI 可以用來指示版本資訊。當與支持使用媒體類型參數指示 schema 的媒體類型一起使用時,這些版本化的 schema URI 可以用於內容協商。
資源可以指示它是多個 schema 的實例,這允許同時支持多個相容的版本。然後,客戶端應用程式可以使用它識別的 hyper-schema,並忽略更新或更舊的版本。
由於 hyper-schema 一次代表一個資源,因此它不提供列舉與連結執行的協定操作的所有可能回應。每個回應(包括錯誤)都被認為是其自身的(可能是匿名的)資源,並且應該識別其自己的 hyper-schema,並且可選地使用適當的媒體類型(例如 RFC 7807 的「application/problem+json」 [RFC7807]),以允許使用者代理或客戶端應用程式解釋協定自身狀態報告之外提供的任何資訊。
可以對一組 hyper-schema 進行靜態分析,而無需實例數據,以便生成諸如文檔或程式碼之類的輸出。但是,在沒有執行階段實例數據的情況下,無法存取驗證和 hyper-schema 的完整功能集。
這是一種故意的設計選擇,旨在為超媒體系統提供最大的執行階段靈活性。作為媒體類型的 JSON Schema 允許建立用於靜態分析和內容生成的其他詞彙表,而本規範中並未涉及這些詞彙表。此外,如果完整設計時描述是目標,則個別系統可能會將其使用限制為可以靜態分析的子集。[CREF13]已提出用於 API 文件和其他用途的詞彙表,歡迎在 https://github.com/json-schema-org/json-schema-vocabularies 上做出貢獻。
[CREF14]此部分將在離開 Internet-Draft 狀態之前移除。