網際網路工程任務組 | H. Andrews, Ed. |
網路草案 | |
預定狀態:資訊性 | A. Wright, Ed. |
失效日期:2020年3月20日 | 2019年9月17日 |
JSON 超媒體綱要:JSON 超媒體註釋詞彙
draft-handrews-json-schema-hyperschema-02
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) 的工作文件。請注意,其他群組也可能將工作文件作為網路草案發布。目前網路草案的列表位於 https://datatracker.ietf.org/drafts/current/。
網路草案是有效期最長六個月的草案文件,且隨時可能由其他文件更新、取代或廢棄。將網路草案用作參考資料或以「工作中」以外的方式引用是不適當的。
本網路草案將於 2020 年 3 月 20 日失效。
版權所有 (c) 2019 IETF Trust 和被認定為文件作者的人員。保留所有權利。
本文件受 BCP 78 以及發佈本文件之日生效的 IETF Trust 關於 IETF 文件的法律條款 (https://trustee.ietf.org/license-info) 的約束。請仔細閱讀這些文件,因為它們說明您在本文件上的權利和限制。從本文件中提取的程式碼元件必須包含「精簡 BSD 授權」文字,如 Trust 法律條款第 4.e 節所述,且不提供「精簡 BSD 授權」中所述的保證。
JSON 超媒體綱要是一種 JSON Schema 詞彙,用於使用超連結和指令註釋 JSON 文件,以透過超媒體環境(如 HTTP)處理和操作遠端 JSON 資源。
術語「JSON 超媒體綱要」用於指使用這些關鍵字的 JSON Schema。術語「超媒體綱要」本身指的是本規範範圍內的 JSON 超媒體綱要。
引入用於指定連結的主要機制是「連結描述物件」(LDO),它是 RFC 8288 第 2 節中定義的抽象連結模型的序列化形式。
本規範將使用 JSON Schema 核心和 JSON Schema 驗證規範定義的概念、語法和術語。建議讀者備有一份這些規範。
本文件中「必須」(MUST)、「不得」(MUST NOT)、「必要」(REQUIRED)、「應當」(SHALL)、「不應當」(SHALL NOT)、「應該」(SHOULD)、「不應該」(SHOULD NOT)、「建議」(RECOMMENDED)、「可以」(MAY) 和「可選」(OPTIONAL) 等關鍵字的解釋應如 RFC 2119 中所述。
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 節,其基本 URI 為「https://example.com/api/」,則「https://example.com/api/thing/1234」是產生的連結的目標 URI。
術語「綱要」、「實例」和「後設綱要」的解釋應如 JSON Schema 核心規範中所定義。
術語「適用」和「附加」的解釋應如 JSON Schema 核心規範第 3.1 節中所定義。
術語「連結」、「連結內容」(或「內容」)、「連結目標」(或「目標」)和「目標屬性」的解釋應如 RFC 8288 第 2 節中所定義。
術語「使用者代理程式」的解釋應如 RFC 7230 第 2.1 節中所定義,該定義已廣義化,以適用於超媒體系統中可能使用的任何協定,而不僅限於 HTTP 用戶端。
本規範定義下列術語
JSON 超級綱要實作能夠接收超級綱要、一個實例,在某些情況下還會接收用戶端輸入,並產生一組完全解析的有效連結。根據RFC 8288 第 2 節的定義,連結包含一個上下文、一個類型化的關係、一個目標,以及可選的其他目標屬性。
關係類型和目標屬性直接取自每個連結的連結描述物件。上下文和目標識別碼是透過 URI 範本、實例資料,以及(在目標識別碼的情況下)用戶端輸入的組合來建構的。
目標始終由 URI 完全識別。由於 application/json 以及許多其他可用於 JSON 超級綱要的媒體類型缺乏 URI 片段識別碼語法,因此上下文可能僅由 URI 部分識別。在這種情況下,剩餘的識別將以 JSON 指標的形式提供。
一些 IANA 註冊的連結關係類型在 JSON 超級綱要文件中具有特定的語義。「self」連結用於與實例文件所代表的資源進行互動,而「collection」和「item」連結則識別可以假定特定於集合的語義的資源。
JSON 超級綱要元綱要目前的 URI 為 <https://json-schema.dev.org.tw/draft/2019-09/hyper-schema#>。
此詞彙(稱為超級綱要詞彙)的目前 URI 為:<https://json-schema.dev.org.tw/draft/2019-09/vocab/hyper-schema>。
對應的元綱要的目前 URI(與上述便利元綱要不同,因為它僅描述超級綱要關鍵字(「base」和「link」))為:<https://json-schema.dev.org.tw/draft/2019-09/meta/hyper-schema>。
連結描述格式可以在沒有 JSON 綱要的情況下使用,並且可以透過將標準連結描述綱要參照為使用連結的資料結構的綱要來宣告此格式的使用。標準連結描述綱要的 URI 為:<https://json-schema.dev.org.tw/draft/2019-09/links#>。
JSON 超級綱要實作可以自由地以任何格式提供輸出。但是,定義了一種特定格式用於一致性測試套件中,該格式也用於說明「實作需求」中的要點,並顯示範例產生的輸出。建議實作能夠以這種格式產生輸出,以促進測試。描述建議的輸出格式的 JSON 綱要的 URI 為 <https://json-schema.dev.org.tw/draft/2019-09/output/hyper-schema#>。
為了修正錯誤,詞彙和元綱要 URI 的更新可能會在規格草案之間發布。實作應考慮在此規格草案之後且在下一個草案之前發布的 URI,以指示與此處列出的語法和語義相同。
適用於實例中位置的所有綱要中的超級綱要關鍵字(如 JSON 綱要核心的 3.1 節所定義)可以與該實例一起使用。
當多個子綱要適用於給定的子實例時,所有「link」陣列都必須以任何順序合併到單一集合中。結果集合中的每個物件都必須保留其自己的適用「base」值列表,並依解析順序從相同的綱要和任何父綱要中取得。
與所有 JSON 綱要關鍵字一樣,本節中描述的所有關鍵字都是選用的。最小的有效 JSON 超級綱要是空白物件。
如果存在,則此關鍵字必須先解析為 URI 範本,然後必須解析為相對於實例的目前 URI 基本位置的 URI 參考。在處理包含「base」的子綱要以及其中的所有子綱要時,結果必須設定為實例的新 URI 基本位置。
在針對「anchor」解析使用時,與針對「href」解析使用時,「base」範本的解析過程可能不同,這在 URI 範本章節中有詳細說明。
綱要的「links」屬性用於將連結描述物件與實例關聯。此屬性的值必須是一個陣列,並且陣列中的項目必須是如下定義的連結描述物件。
連結描述物件 (LDO) 是 RFC 8288 第 2 節中定義的抽象連結模型的序列化。如該文件中所述,連結包含一個上下文、一個關係類型、一個目標,以及可選的目標屬性。JSON 超級綱要的 LDO 提供所有這些,以及使用 JSON 綱要以各種方式描述連結輸入的額外功能。
由於使用 URI 範本來識別連結上下文和目標,以及在識別目標時可選地進一步使用用戶端輸入,因此 LDO 是一個連結範本,當與 JSON 實例文件一起使用時,可能會解析為多個連結。
LDO 的特定使用,通常涉及跨協定的請求和回應,稱為操作。對於許多協定,在任何給定連結上都可能進行多項操作。協定由目標的 URI 方案指示。請注意,並非所有 URI 方案都指示可用於通訊的協定,即使是具有指示此類協定的 URI 方案的資源,也不一定可以透過該協定取得。
連結描述物件必須是一個物件,並且必須存在 「href」和 「rel」屬性。本節將簡要介紹每個關鍵字,並在本文檔的後面提供其他使用說明和全面的範例。
在 JSON 超級綱要中,連結的上下文資源預設為附加它的子實例(如JSON 綱要核心規格的 3.1 節所定義)。這通常不是整個實例文件。可以使用本節中的關鍵字來變更此預設上下文。
根據實例的媒體類型,可能無法將 URI 指派給確切的預設上下文資源。尤其是,application/json 沒有定義 URI 片段解析語法,因此純 JSON 文件中的屬性或陣列元素無法由 URI 完全識別。當無法產生完整的 URI 時,上下文的位置應透過實例文件的 URI 以及單獨的純字串 JSON 指標來傳達。
實作必須能夠建構連結上下文的 URI,以及(如果需要完全識別),按照RFC 6901 第 5 節的規定以字串表示形式取代 URI 片段的 JSON 指標。根據 URI 範本建構 URI 的過程在 URI 範本章節中給出。
此屬性設定連結的上下文 URI。此屬性的值是 URI 範本,並且產生的 URI 參考必須根據實例的基本 URI 解析。
URI 是使用為 「href」屬性描述的相同過程從提供的 URI 範本計算而來,但 「hrefSchema」不得應用除外。與目標 URI 不同,上下文 URI 不接受使用者輸入。
此屬性會變更實例中被視為連結上下文資源的點。此屬性的值必須是 JSON 字串表示形式的有效 JSON 指標,或是相對於預設上下文評估的有效相對 JSON 指標。
雖然具有已知 URI 的替代上下文最好使用 「anchor」關鍵字設定,但由於 application/json 缺少片段識別碼語法,因此通常無法使用 URI 變更 JSON 實例中的上下文。
即使在將 JSON 指標定義為片段識別碼語法的「+json」媒體類型中,如果預設上下文巢狀在陣列中,也無法取得預設上下文在該陣列中的位置索引,以便建構指向該相同巢狀 JSON 物件中另一個屬性的指標。這將在範例中示範。
如果實例的媒體類型允許使用此類片段,則處理此關鍵字的結果應為 URI 片段。否則,它必須是一個字串編碼的 JSON 指標。
連結的關係類型識別其語義。它是傳達應用程式如何與資源互動的主要方式。
關係定義通常不依賴媒體類型,並且鼓勵使用者使用最適合的現有已接受關係定義。
此屬性的值必須是字串或字串陣列。如果值是陣列,則它必須至少包含一個字串。
每個字串**必須**是 RFC 8288 第 2.1 節中定義的單一連結關係類型,包括基於是否存在其他連結關係類型,**不應**推斷額外語義的限制。
此屬性為必填。
「self」連結,如 RFC 4287 的第 4.2.7.2 節 最初定義的,表示目標 URI 識別與連結上下文等效的資源。在 JSON Hyper-Schema 中,「self」連結**必須**可以從實例解析,因此**不得**存在「hrefSchema」。
Hyper-schema 作者**應該**使用「templateRequired」來確保「self」連結具有所需的所有實例資料以供使用。
hyper-schema 實作**必須**識別出關係類型為「self」的連結,其將整個目前的實例文件作為其上下文,描述使用者代理如何與該實例文件所代表的資源互動。
RFC 6573 定義並註冊了「item」和「collection」連結關係類型。JSON Hyper-Schema 對這些類型指示的集合資源施加了額外的語義。
實作**必須**將「collection」連結的目標和「item」連結的上下文識別為集合。
在超媒體中,一種廣為人知的設計模式是使用集合資源來建立集合的成員,並為其指定伺服器指派的 URI。如果 URI 方案指示的協定定義了一種適合使用伺服器指派的 URI 建立資源的特定方法,則由這些連結關係類型識別的集合資源**不得**為該方法定義與建立集合成員的語義相衝突的語義。集合資源**可以**透過此類協定方法實作項目建立,並且使用者代理**可以**假設任何此類操作(如果存在)都具有項目建立的語義。
由於此類方法將對應於 JSON Hyper-Schema 的資料提交概念,因此連結的 「submissionSchema」 欄位**應該**與集合項目的表示的 schema 相容,如「item」連結的目標資源或「collection」連結的上下文資源的「self」連結所指示。
當沒有註冊的關係(除了「related」)適用時,鼓勵使用者鑄造自己的擴展關係類型,如 RFC 8288 的第 2.1.2 節 中所述。選擇連結關係類型 URI 的最簡單方法是使用已在使用的 URI 方案來識別系統的主要資源,或使用人類可讀、不可取值解析的 URI 方案,例如 RFC 4151 定義的「tag」。
擴展關係類型 URI 不需要可取值解析,即使在使用允許取值解析的方案時也是如此。
目標 URI 模板用於識別連結的目標,可能會使用實例資料。此外,使用 「hrefSchema」,此模板可以根據用戶端輸入識別一組可能使用的目標資源。解析 URI 模板的完整過程(無論是否有用戶端輸入)將在 URI 模板化 章節中介紹。
「href」連結描述屬性的值是用於確定相關資源的目標 URI 的模板。實例屬性的值**必須**解析為相對於實例的基本 URI 的 URI 參考。
此屬性為**必填**。
本節中的關鍵字在解析 hyper-schema 中涉及的所有 URI 模板時使用:「base」、「anchor」和「href」。 有關完整的模板解析演算法,請參閱 URI 模板化 章節。
請注意,在解析「base」模板時,解析開始的附加點是要解析的「href」或「anchor」關鍵字的附加點,它需要解析「base」模板,而不是「base」關鍵字本身的附加點。
「templatePointers」連結描述屬性的值**必須**是一個物件。物件中的每個屬性值**必須**是一個有效的 JSON 指標,或一個有效的 相對 JSON 指標,其相對於正在解析模板的連結的附加點進行評估。
對於物件中與正在解析的模板中的變數名稱相符的每個屬性名稱,該屬性的值會調整該變數的變數解析起始位置。與正在解析的模板中的模板變數名稱不符的屬性**必須**被忽略。
此關鍵字的值**必須**是一個陣列,且元素**必須**是唯一的。每個元素**應該**與連結的 URI 模板中的變數相符,不帶百分比編碼。在完成整個 URI 模板解析過程之後,如果此陣列中存在的任何變數沒有值,則**不得**使用該連結。
本節中的所有屬性僅供參考。雖然「title」和「description」等關鍵字主要用於向使用者呈現連結,但預測連結互動或回應性質的那些關鍵字**不得**被視為權威。目標資源的執行時行為**必須**在與 LDO 中的目標屬性衝突時受到尊重。
此屬性定義連結的標題。該值**必須**是一個字串。
使用者代理**可以**在向使用者呈現連結時使用此標題。
此屬性提供標題中不存在的額外資訊。該值**必須**是一個字串。雖然標題最好簡短,但可以使用描述來更詳細地說明連結的目的和用法。
使用者代理**可以**在向使用者呈現連結時使用此描述。
此屬性的值表示 RFC 2046 的媒體類型,預期在擷取此資源時返回。此屬性值**可以**改為使用 RFC 7231 第 5.3.2 節 - HTTP "Accept" 標頭 中定義的相同模式,作為媒體範圍。
此屬性類似於其他連結序列化格式的「type」屬性。使用者代理**可以**使用此資訊在連結被追蹤之前通知他們向使用者呈現的介面,但**不得**在解釋結果資料時使用此資訊。相反,使用者代理**必須**使用回應提供的媒體類型進行執行時解釋。有關誤用「targetMediaType」的詳細檢視,請參閱「安全考量」部分。
對於支援內容協商的協定,實作**可以**選擇使用 「headerSchema」 中的協定特定資訊來描述可能的目標媒體類型。如果同時存在協定特定資訊和「targetMediaType」,則「targetMediaType」的值**必須**與協定特定資訊相容,並且**應該**指示在沒有內容協商的情況下將返回的媒體類型。
當沒有此類協定特定資訊可用時,或當實作無法識別所涉及的協定時,則該值**應該**被視為「application/json」。
此屬性提供一個 schema,預期用於描述連結目標的表示。根據協定,schema 可能會或可能不會描述對使用該連結執行的任何特定操作的回應或要求。有關此關鍵字如何與 HTTP 一起使用的深入討論,請參閱 JSON Hyper-Schema 和 HTTP 章節。
此屬性的值僅供參考。它表示預期透過與目標資源互動而發現的資訊,通常以協定特定的控制資訊或中繼資料的形式,例如回應 HTTP HEAD 或 OPTIONS 請求時返回的標頭。協定由「href」URI 方案決定,但請注意,不保證資源可以透過此類協定存取。
此屬性的值**應該**是一個物件。此物件的鍵**應該**是控制資料欄位名稱的小寫形式。每個值**應該**是一個陣列,以便統一處理多值欄位。多個值**必須**呈現為陣列,而不是單一字串。
控制資訊不適合表示為 JSON 物件的協定**可以**由另一個資料類型表示,例如陣列。
JSON Hyper-Schema 實作**必須**忽略無法理解為指示協定一部分的值。應用程式**可以**使用此類值,但**不得**假設與其他實作的互操作性。
實作**不得**假設此物件中已說明所有可發現的資訊。用戶端應用程式**必須**正確處理與此屬性值相矛盾的執行時回應。
用戶端應用程式**不得**假設實作將根據此屬性的值自動採取任何動作。
有關將此關鍵字與 HTTP 和類似協定一起使用的指南,請參閱 「JSON Hyper-Schema 和 HTTP」。
有四種方式可以將用戶端輸入與連結一起使用,每種方式都由單獨的連結描述物件關鍵字處理。在執行操作時,使用者代理**應該**忽略與其語義無關的 schema。
「hrefSchema」連結描述屬性的值**必須**是一個有效的 JSON Schema。此 schema 用於驗證使用者輸入或其他使用者代理資料,以填寫 「href」 中的 URI 模板。
省略「hrefSchema」或將整個 schema 設定為「false」會阻止接受任何使用者代理資料。
將適用於特定變數的任何子模式設定為 JSON 字面值 "false",會阻止任何使用者代理程式資料被該單一變數接受。
對於可以從實例資料解析的樣板變數,如果實例資料對於 "hrefSchema" 中所有適用的子模式都有效,則必須使用它來預先填入該變數的輸入資料集。
請注意,即使資料是從實例預先填入的,"hrefSchema" 中該變數的驗證模式也不必與適用於實例資料位置的驗證模式相同。這允許針對使用者代理程式資料使用不同的驗證規則,例如支援日期時間輸入中使用文字拼寫的月份,但使用標準日期時間格式進行儲存。
接受輸入後(可能會覆蓋預先填入的實例資料),產生的資料集必須成功通過 "hrefSchema" 值的驗證。如果沒有通過驗證,則不得使用該連結。如果驗證有效,則 "URI 樣板化" 區段中給定的流程將繼續使用此更新的資料集。
[CREF2]如同 "targetHints",此關鍵字在某種程度上規範不足,旨在鼓勵實驗和回饋,以便我們在彈性和清晰度之間取得平衡。
如果存在,此屬性是特定於協定的請求標頭或類似的控制和中繼資料的模式。此物件的值必須是有效的 JSON Schema。協定由 "href" URI 方案決定,但請注意,不能保證資源可透過此類協定存取。該模式僅供參考;目標資源的行為不受其存在的約束。
此關鍵字的目的是宣傳目標資源互動功能,並向使用者代理程式和客戶端應用程式指示哪些標頭和標頭值可能有用。使用者代理程式和客戶端應用程式可以使用該模式來驗證相關的標頭,但不應假設缺少標頭或值就禁止使用。雖然模式作者可以將 "additionalProperties" 設定為 false,但不建議這樣做,而且不能阻止客戶端應用程式或使用者代理程式在發出請求時提供額外的標頭。
JSON 資料模型到標頭的確切對應是協定相關的。但是,在大多數情況下,此模式應指定 "object" 類型,並且屬性名稱應為控制資料欄位名稱的小寫形式。與 "targetHints" 一樣,這些值應描述為陣列,以允許有多個值,即使只預期一個值也是如此。
請參閱"JSON Hyper-Schema 和 HTTP" 區段,以取得有關將此關鍵字用於 HTTP 和類似協定的詳細指南。
"headerSchema" 適用於協定支援的任何請求方法或命令。產生請求時,使用者代理程式和客戶端應用程式應忽略與該請求不相關的標頭模式。
在 JSON Hyper-Schema 中,"targetSchema" 提供目標資源表示的非權威性描述。客戶端應用程式可以使用 "targetSchema" 來構建用於替換或修改表示的輸入,或作為基於修補媒體類型建立修補文件的基礎表示。
或者,如果缺少 "targetSchema",或者客戶端應用程式偏好僅使用權威性資訊,則它可以與目標資源互動以確認或發現其表示結構。
"targetSchema" 並非旨在描述連結操作回應,除非回應語義指示它是目標資源的表示。在所有情況下,回應本身指示的模式是權威性的。請參閱"JSON Hyper-Schema 和 HTTP" 以取得詳細範例。
"submissionSchema" 和 "submissionMediaType" 關鍵字描述由目標資源實作的處理函數的網域。否則,如上所述,提交模式和媒體類型對於與它們不相關的操作會被忽略。
如果存在,此屬性指示客戶端應用程式和使用者代理程式應使用哪種媒體類型格式來處理由 "submissionSchema" 描述的請求有效負載。
省略此關鍵字的效果與值為 application/json 相同。
請注意,"submissionMediaType" 和 "submissionSchema" 不僅限於 HTTP URI。[CREF3]此陳述可能會移到範例結束的地方。
此屬性包含一個模式,該模式定義了根據 "submissionMediaType" 屬性編碼並傳送至目標資源進行處理的文件的可接受結構。這可以視為描述由目標資源實作的處理函數的網域。
這與 "targetSchema" 屬性是不同的概念,後者描述目標資訊資源(包括在 PUT 請求中替換資源的內容),而 "submissionSchema" 描述要由資源評估的使用者提交的請求資料。"submissionSchema" 適用於具有不一定以目標表示形式定義的有效負載的請求。
省略 "submissionSchema" 的效果與值為 "true" 相同。
在較高的層次上,符合規範的實作將滿足以下要求。每個要求在各個關鍵字區段和關鍵字組概觀中都有更詳細的說明。
請注意,關於實作如何必須識別 "self"、"collection" 和 "item" 連結的要求已在連結關係類型區段中完整說明,此處不再重複。
雖然它不是實作的強制格式,但測試套件中使用的輸出格式會總結每個連結在使用之前需要計算的內容
其他未參與產生上述資訊的 LDO 關鍵字會以它們在產生測試套件輸出時的確切顯示方式包含在內。這些欄位在此處不再進一步討論,除非它們特別相關。
在使用連結之前,必須先將超模式應用於實例並找到所有適用且有效的連結,才能探索這些連結。請注意,除了收集有效的連結之外,任何解析每個 LDO 的 URI 樣板所必需的 "base" 值也必須被找到,並透過最適合實作 URI 樣板解析過程的任何機制與 LDO 相關聯。
實作必須支援透過附加指標或上下文指標來查閱連結,方法是執行查閱或提供一組已確定兩個指標的所有連結,以便使用者代理程式可以自行實作查閱。
當依上下文指標執行查閱時,附加到相同陣列元素上的連結必須按照它們所附加的陣列元素的相同順序傳回。
三個超模式關鍵字是URI 樣板:"base"、"anchor" 和 "href"。每個關鍵字都會分別解析為 URI 參考,然後針對基礎解析錨點或 href URI 參考(基礎本身會根據需要針對較早的基礎解析,每個基礎都首先從 URI 樣板解析為 URI 參考)。
所有三個關鍵字都共用相同的演算法,用於從實例資料解析變數,該演算法會使用 "templatePointers" 和 "templateRequired" 關鍵字。當解析 "href" 時,它和任何解析為絕對 URI 所需的 "base" 樣板,演算法都會進行修改,以選擇性地接受基於 "hrefSchema" 關鍵字的使用者輸入。
對於每個 URI 樣板 (T),以下虛擬程式碼描述了將 T 解析為 URI 參考 (R) 的演算法。對於此演算法的目的
此演算法應首先應用於 "href" 或 "anchor",然後根據需要應用於每個後續的 "base"。順序很重要,因為並非總是能夠判斷樣板將解析為完整的 URI 還是 URI 參考。
用英語來說,高階演算法是
這是虛擬程式碼形式的高階演算法。"T" 來自 LDO 中的 "href" 或 "anchor",或來自包含模式中的 "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" 代表任何適當的輸入機制。這可以是實體的網頁表單,也可以是更程式化的結構,例如接受特定欄位和資料類型以及指定初始值(如果有的話)的回呼函式。
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: templateData[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 作者應牢記這一點,如果確切的表示形式很重要,則應使用其他類型(例如字串或布林值)。如果數字是以字串形式提供作為輸入,則應使用作為輸入的字串。
對於給定的連結,實作必須使所有目標屬性關鍵字的值直接提供給使用者代理程式。實作可以提供額外的介面來使用此資訊,如每個關鍵字的部分所述。
對於給定的連結,實作必須使每個輸入 schema 關鍵字的值直接提供給使用者代理程式。
為了鼓勵 URI 範本解析過程的封裝,實作可以省略僅用於建構 URI 的 LDO 關鍵字。但是,實作必須提供對連結關係類型的存取。
未識別的關鍵字應提供給使用者代理程式,並且必須忽略其他情況。
hyper-schema 實作應提供建構對目標資源的任何有效請求所需的所有資訊。
LDO 可以表達執行連結上的任何操作所需的所有資訊。本節說明使用者代理程式應檢查哪些 hyper-schema 欄位,以從實例資料和用戶端輸入的任何組合中建構請求。hyper-schema 實作本身不應建構和傳送請求。
目標 URI 建構規則(包括用於接受輸入的 "hrefSchema")對於所有可能的請求都相同。
不攜帶主體酬載的請求不需要額外的關鍵字支援。
將目標表示形式作為酬載的請求應使用 "targetSchema" 和 "targetMediaType" 關鍵字來描述輸入和驗證酬載。如果協定允許執行採用以媒體類型修改的表示形式為基礎的酬載的操作(例如修補媒體類型),則應以協定特定的方式透過 "targetHints" 指示此類媒體類型。
採用非衍生自目標資源表示形式的酬載的請求應使用 "submissionSchema" 和 "submissionMediaType" 關鍵字來描述輸入和驗證酬載。超媒體中使用的協定通常每個連結僅支援一個此類非表示形式操作。
透過單一超媒體協定操作來傳輸多個具有任意不同請求結構的應用程式操作的 RPC 系統,不屬於 JSON Hyper-Schema 等超媒體格式的範圍。
作為一種超媒體格式,JSON Hyper-Schema 關注於描述資源,包括詳細描述其連結,以發出所有有效的請求。它不關注直接描述這些請求的所有可能回應。
與任何超媒體系統一樣,回應應是自我描述的。在 hyper-schema 的上下文中,這表示每個回應都必須連結其自己的 hyper-schema。雖然預期包含目標資源表示形式的回應會根據 "targetSchema" 和 "targetMediaType" 進行驗證,但這些關鍵字僅供參考,如果與回應本身矛盾,則必須忽略。
其他回應,包括錯誤回應、複雜的重新導向和處理狀態表示形式,也應連結到它們自己的 schema 並使用適當的媒體類型(例如,用於錯誤的 "application/problem+json")。某些錯誤可能不會連結 schema,因為它們是由不知道 hyper-schema 的中繼節點產生,而不是由原始節點產生。
預期使用者代理程式應充分理解協定狀態碼和回應媒體類型,以處理常見情況,並向用戶端應用程式提供足夠的資訊來處理特定於網域的回應。
在設計時靜態地對應所有可能的回應及其 schema 超出了 JSON Hyper-Schema 的範圍,但可能在其他建構於 hyper-schema 之上的 JSON Schema 詞彙表的範圍內(請參閱 附錄 A.3)。
在與串流解析器搭配使用時,基於連結的上下文探索連結或使用連結的上下文來識別集合的要求,會帶來獨特的挑戰。如果不處理整個 schema 和實例文件,就不可能權威地滿足這些要求。
此類實作可以選擇根據迄今處理的資料返回非權威的答案。在提供此方法時,實作必須清楚說明回應的性質,並且必須提供一個選項來封鎖並等待直到處理完所有資料並返回權威的答案。
雖然 JSON Hyper-Schema 是一種超媒體格式,因此與協定無關,但預期它最常見的用途將是在 HTTP 系統中,或使用與 HTTP 明確類似的 CoAP 等協定的系統中。
本節提供了關於如何將每個常見的 HTTP 方法與連結一起使用,以及集合資源如何對 HTTP POST 施加額外的約束的指南。此外,還提供了關於暗示 HTTP 回應標頭值和描述與給定資源相關的可能 HTTP 請求標頭的指南。
JSON Schema 核心規格的第 13 節提供了關於在超媒體系統中將實例連結到其 schema 的指南。這可以使用網路可存取的 schema 完成,或者可以簡單地識別預先封裝在用戶端應用程式中的 schema。JSON Hyper-Schema 有意不限制此機制,但建議盡可能使用核心規格中概述的技術。
連結描述物件不直接指示目標資源支援哪些操作,例如 HTTP 方法。相反,操作應主要從連結關係類型和 URI 配置中推斷。
這表示對於每個目標資源和連結關係類型配對,schema 作者應僅定義一個 LDO。雖然可以使用 "allow" 和 "targetHints" 來重複具有不同 HTTP 方法標記為允許的關係類型和目標配對,但不建議這樣做,且符合規範的實作可能無法很好地支援。
使用每個 HTTP 方法所需的所有資訊都可以在單一 LDO 中傳達,如本節所述。"targetHints" 中的 "allow" 欄位僅旨在提示支援哪些操作,而不是單獨定義每個操作。
但是請注意,資源始終可以在執行階段拒絕操作,例如由於授權失敗,或由於控制操作可用性的其他應用程式狀態。
"targetSchema" 描述連結目標端的資源,而 "targetMediaType" 定義該資源的媒體類型。對於 HTTP 連結,"headerSchema" 也可用於描述在 "Accept" 請求標頭中使用的有效值,該標頭可以支援多種媒體類型或媒體範圍。當同時存在兩種指示目標媒體類型的方式時,"targetMediaType" 應指示預設表示形式媒體類型,而 "headerSchema" 中 "accept" 的 schema 應包含預設值以及可以請求的任何替代媒體類型或媒體範圍。
由於許多 HTTP 方法的語義是根據目標資源定義的,因此 "targetSchema" 用於多個 HTTP 方法的請求和/或回應。特別是,"targetSchema" 建議用戶端應用程式可以預期 HTTP GET 的回應或 "Content-Location" 標頭等於請求 URI 的任何回應,以及如果用戶端應用程式使用 HTTP PUT 請求建立或取代資源時應傳送的內容。這些關聯由 RFC 7231,第 4.3.1 節 - "GET",第 4.3.4 節 "PUT" 和第 3.1.4.2 節 "Content-Location" 定義。
根據 RFC 5789,HTTP PATCH 的請求結構由 "targetSchema" 和請求媒體類型的組合確定,該媒體類型由 "Accept-Patch" 標頭傳達,該標頭可以包含在 "targetHints" 中。適用於 PATCH 的媒體類型定義了用於表達文件變更的語法,可以將其應用於 "targetSchema" 描述的表示形式,以確定語法上有效的請求酬載集。通常,驗證 PATCH 請求的最簡單方法是應用它並驗證結果是否為正常表示形式。
JSON Hyper-Schema 允許處理任意資料的資源,而不是使用目標的表示形式。此任意資料由 "submissionSchema" 和 "submissionMediaType" 關鍵字描述。在 HTTP 的情況下,POST 方法是唯一處理此類資料的方法。雖然有一些關於將 POST 與集合一起使用的慣例,但 POST 請求的語義是由目標資源定義的,而不是 HTTP。
除了協定中性的 "submission*" 關鍵字(有關非 HTTP 範例,請參閱 第 9.3 節)之外,"Accept-Post" 標頭可用於指定必要的媒體類型,並且可以透過 "targetHints" 欄位發佈。[CREF4]如果兩者都使用會發生什麼?此外,"submissionSchema" 是必須支援的,而 "targetHints" 最多是應支援的。但是,禁止在 "targetHints" 中使用 "Accept-Post" 似乎不正確。
對 POST 的成功回應(201 或設定了 "Content-Location" 的 200 除外)同樣沒有 HTTP 定義的語義。與所有 HTTP 回應一樣,回應中的任何表示形式都應連結到其自己的 hyper-schema,以指示如何處理它。如附錄 A.2 中所述,將超連結與所有可能的操作回應連結起來不屬於 JSON Hyper-Schema 的範圍。
HTTP 回應標頭資訊的 JSON 序列化應遵循正在進行的工作 "A JSON Encoding for HTTP Header Field Values" 中建立的指南。該文件中範例中顯示的方法應盡可能應用於其他結構類似的標頭。
所有可能的 HTTP 方法回應的標頭都共用「headerSchema」。特別是,HEAD 回應中出現的標頭和 OPTIONS 回應中出現的標頭都可以出現。「headerSchema」內部不會區分哪個標頭包含在哪個方法回應中。
建議 schema 作者在適用的情況下,提供以下類型 HTTP 標頭值的提示
一般而言,在不同時間可能具有不同值的標頭不應包含在「targetHints」中。
例如,允許 HEAD、GET 和 POST 的 Allow 標頭將顯示如下
{ "targetHints": { "allow": ["HEAD", "GET", "POST"] } }
請注意,無論是包含以逗號分隔值的單行 Allow 標頭、每行具有一個值的多個 Allow 標頭,還是任何此類安排的組合,其表示方式都相同。如同 HTTP 標頭通常的情況一樣,以逗號分隔的值和標頭的多個出現被視為相同。
schema 應編寫為描述遵循正在進行的 「HTTP 標頭欄位值的 JSON 編碼」中建立的準則的 JSON 序列化。該文件中範例中顯示的方法應盡可能應用於其他類似結構的標頭。
建議 schema 作者在適用的情況下,描述以下類型 HTTP 標頭的可用用法
諸如快取控制和條件請求標頭之類的標頭通常由中介機構而不是資源實施,因此通常不適合描述。雖然資源必須提供使用條件請求所需的信息,但此類標頭和相關回應的執行時間處理不是特定於資源的。
當使用 HTTP 或明確類似於 HTTP 的協議(例如 CoAP)時,這是通過將要建立的個別資源的表示 POST 到集合資源來完成的。識別集合和項目資源的過程在 第 6.2.3 節中描述。
JSON Hyper-Schema 有助於 HTTP 內容協商,並允許主動和被動策略的混合。如上所述,超 schema 可以使用「headerSchema」關鍵字包含 HTTP 標頭(例如「Accept」、「Accept-Charset」、「Accept-Language」等)的 schema。用戶代理或客戶端應用程式可以使用此 schema 中的資訊(例如支援的語言枚舉列表),而不是發出初始請求來啟動被動協商過程。
這樣,可以通過伺服器關於可能值的資訊來告知設定這些標頭的主動內容協商技術,類似於檢查被動協商中的替代方案列表。
對於允許將 schema 指定為媒體類型參數的媒體類型,「Accept」值在請求中發送或在「headerSchema」中宣傳時,可以包含預期協商表示要符合的 schema 的 URI。內容協商中 schema 參數的一個可能用途是,如果資源隨著時間的推移符合多個不同的 schema 版本。客戶端應用程式可以通過這種方式在「Accept」標頭中指示它理解的版本。
本節說明如何使用構造 URI 和 JSON 指標的關鍵字。結果以測試套件使用的格式顯示。[CREF6]需要發布並連結它,但對於現階段審查事情的你們來說,它應該是不言自明的。
大多數其他關鍵字要么是直接的(「title」和「description」),要么是對特定種類的輸入、請求或回應應用驗證,要么具有特定於協議的行為。展示 HTTP 用法的範例在附錄中提供。
對於這個範例,我們將假設一個範例 API,其記錄的入口點 URI 為 https://example.com/api,這是一個空的 JSON 物件,其中包含指向 schema 的連結。這裡,入口點本身沒有任何資料,僅存在以提供一組初始連結
GET https://example.com/api HTTP/1.1 200 OK Content-Type: application/json Link: <https://schema.example.com/entry>; rel="describedBy" {}
連結的超 schema 定義了 API 的基本 URI,並提供兩個連結:「about」連結到 API 文件,以及指示這是基本 URI 的 schema 的「self」連結。
{ "$id": "https://schema.example.com/entry", "$schema": "https://json-schema.dev.org.tw/draft/2019-09/hyper-schema", "base": "https://example.com/api/", "links": [ { "rel": "self", "href": "../api", }, { "rel": "about", "href": "docs" } ] }
這些是最簡單的連結,只有關係類型和沒有範本變數的「href」。它們解析如下
由於 RFC 3986 URI 引用解析演算法的怪癖,「api」在基礎和「self」連結中的「../api」href 中重複。為了使相對 URI 引用在一般情況下良好地工作,基本 URI 需要包含尾部斜線。「about」連結及其「docs」href 顯示了相對引用的常見情況,該引用用於本文檔中的其他範例。
但是,如果 API 對其資源使用沒有尾部斜線的 URI,則無法提供僅刪除尾部斜線而不重複其上方路徑組件的相對引用。這使得入口點資源的情況有些尷尬,它僅在尾部斜線方面與基本 URI 不同。
當然,資源 URI 可能具有尾部斜線,但此範例旨在突出這個經常令人困惑的特殊情況。
[ { "contextUri": "https://example.com/api", "contextPointer": "", "rel": "self", "targetUri": "https://example.com/api", "attachmentPointer": "" }, { "contextUri": "https://example.com/api", "contextPointer": "", "rel": "about", "targetUri": "https://example.com/api/docs", "attachmentPointer": "" } ]
附件指標是根指標(對於實例的空物件而言是唯一可能性)。內容 URI 是預設值,即請求的文件。由於 application/json 不允許片段,因此內容指標對於完整描述內容是必要的。它的預設行為與附件指標相同。
讓我們向系統添加「things」,從一個單獨的 thing 開始
{ "$id": "https://schema.example.com/thing", "$schema": "https://json-schema.dev.org.tw/draft/2019-09/hyper-schema", "base": "https://example.com/api/", "type": "object", "required": ["data"], "properties": { "id": {"$ref": "#/$defs/id"}, "data": true }, "links": [ { "rel": "self", "href": "things/{id}", "templateRequired": ["id"], "targetSchema": {"$ref": "#"} } ], "$defs": { "id": { "type": "integer", "minimum": 1, "readOnly": true } } }
我們的「thing」有一個伺服器分配的 id,這是構造「self」連結所必需的。它還有一個「data」欄位,可以是任何類型。「$defs」部分的原因將在下一個範例中清楚說明。
請注意,驗證 schema 不需要「id」,但 self 連結需要它。這是有道理的:「thing」只有在已建立且伺服器已分配 id 時才具有 URI。但是,您可以將此 schema 與僅包含資料欄位的實例一起使用,這允許您驗證您即將建立的「thing」實例。
讓我們在我們的入口點 schema 中添加一個連結,如果您可以將其 id 作為輸入,則可以直接跳到特定的 thing。為了節省空間,僅顯示新的 LDO。與「self」和「about」不同,沒有關於假設事物的 IANA 註冊關係,因此使用 「tag:」URI 方案定義了擴展關係
{ "rel": "tag:rel.example.com,2017:thing", "href": "things/{id}", "hrefSchema": { "required": ["id"], "properties": { "id": {"$ref": "thing#/$defs/id"} } }, "targetSchema": {"$ref": "thing#"} }
此處的「href」值相同,但其他所有內容都不同。回想一下,實例是一個空物件,因此無法從實例資料解析「id」。相反,它需要作為客戶端輸入。此 LDO 也可以使用「templateRequired」,但在「hrefSchema」中使用「required」並非絕對必要。在「hrefSchema」中未將「id」標記為 required 的情況下提供「templateRequired」將導致錯誤,因為客戶端輸入是解析此連結的唯一可能來源。
此範例涵蓋使用「submission」欄位進行非表示形式輸入,以及將它們與使用輸入解析 URI 範本一起使用。與需要構造 URI 或發送有效負載的 HTML 表單不同,JSON Hyper-Schema 可以描述同一連結上相同操作的兩種輸入。
「submissionSchema」和「submissionMediaType」欄位用於描述不是目標資源表示形式的有效負載。當與「http(s)://」URI 一起使用時,它們通常指 POST 請求有效負載,如 關於 HTTP 用法的附錄中所見。
在這種情況下,我們使用「mailto:」URI,根據 RFC 6068,第 3 節,它不提供任何檢索資源的操作。它只能用於構造要發送的消息。由於沒有可檢索、可替換或可刪除的目標資源的概念,因此不使用「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/2019-09/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://example.com/api/stuff」檢索的以下實例
{ "title": "The Awesome Thing", "stuffWorthEmailingAbout": "Lots of text here...", "email": "[email protected]" }
我們可以在向客戶端應用程式請求輸入之前,部分解析連結如下。
{ "contextUri": "https://example.com/api/stuff", "contextPointer": "", "rel": "author", "hrefInputTemplates": [ "mailto:[email protected]?subject={title}{&cc}" ], "hrefPrepopulatedInput": { "title": "The Awesome Thing" }, "attachmentPointer": "" }
請注意,使用「href*」關鍵字代替「targetUri」。這些是三種可能的「targetUri」值,涵蓋不同種類的輸入。以下是每個範例
連結是從上下文資源到目標資源的類型化連線。較舊的連結序列化支援「rev」關鍵字,它採用與「rel」相同的連結關係類型,但反轉了語意。這早已被棄用,因此 JSON Hyper-Schema 不支援它。相反地,「anchor」變更上下文 URI 的能力可以用來反轉連結的方向。它也可以用來描述兩個資源之間的連結,這兩個資源都不是當前資源。
例如,有一個 IANA 註冊的「up」關係,但沒有「down」。在 HTTP Link 標頭中,你可以將「down」實作為 「rev」:「up」。
首先,讓我們看看如何在 HTTP 中完成此操作,顯示一個「self」連結和兩個語義相同的連結,一個使用「rev」:「up」,另一個使用「anchor」和「rel」:「up」(由於格式限制而換行)。
GET https://example.com/api/trees/1/nodes/123 HTTP/1.1 200 OK Content-Type: application/json Link: <https://example.com/api/trees/1/nodes/123>; rel="self" Link: <https://example.com/api/trees/1/nodes/123>; rel="up"; anchor="https://example.com/api/trees/1/nodes/456" Link: <https://example.com/api/trees/1/nodes/456>; rev="up" { "id": 123, "treeId": 1, "childIds": [456] }
請注意,「rel=up」連結的目標 URI 與「rel=self」連結相同,並將「anchor」(它識別連結的上下文)設定為子系的 URI。當「self」連結也存在時,工具很容易偵測到這種反向連結。
以下超結構描述套用於上述回應中的實例,將產生相同的「self」連結和帶有「anchor」的「up」連結。它還顯示了範本化的「base」URI 的使用,以及「templatePointers」中的絕對和相對 JSON 指標。
{ "$id": "https://schema.example.com/tree-node", "$schema": "https://json-schema.dev.org.tw/draft/2019-09/hyper-schema", "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}" } ] }
「base」範本針對目標(「href」)和上下文(「anchor」)URI 的評估方式相同。
請注意使用的兩種不同類型的 templatePointers。「thisNodeId」對應到絕對 JSON 指標「/id」,而「childId」對應到相對指標「0」,它表示目前項目的值。絕對 JSON 指標不支援任何類型的萬用字元,因此沒有辦法在沒有相對 JSON 指標的情況下指定類似「目前項目」的概念。
在許多系統中,個別資源會分組為集合。這些集合也經常提供一種方式來建立具有伺服器指派識別碼的個別項目資源。
在此範例中,我們將重複使用前面章節中顯示的個別事物綱要。為了方便起見,這裡重複說明,並新增一個「collection」連結,其「targetSchema」參考指向我們接下來要介紹的集合綱要。
{ "$id": "https://schema.example.com/thing", "$schema": "https://json-schema.dev.org.tw/draft/2019-09/hyper-schema", "base": "https://example.com/api/", "type": "object", "required": ["data"], "properties": { "id": {"$ref": "#/$defs/id"}, "data": true }, "links": [ { "rel": "self", "href": "things/{id}", "templateRequired": ["id"], "targetSchema": {"$ref": "#"} }, { "rel": "collection", "href": "/things", "targetSchema": {"$ref": "thing-collection#"}, "submissionSchema": {"$ref": "#"} } ], "$defs": { "id": { "type": "integer", "minimum": 1, "readOnly": true } } }
「collection」連結對所有項目都相同,因此沒有 URI 範本變數。「submissionSchema」是項目本身的。如第 6.2.3 節中所述,如果「collection」連結支援提交機制(HTTP 中的 POST),則它必須實作項目建立語意。因此,「submissionSchema」是透過此連結建立「thing」的綱要。
現在我們想要描述「thing」的集合。此綱要描述一個集合,其中每個項目表示法都與個別「thing」表示法相同。雖然許多集合表示法僅包含項目表示法的子集,但此範例使用全部內容來最大限度地減少所涉及的綱要數量。實際的集合項目以物件內部的陣列形式出現,我們將在下一個範例中向物件新增更多欄位。
{ "$id": "https://schema.example.com/thing-collection", "$schema": "https://json-schema.dev.org.tw/draft/2019-09/hyper-schema", "base": "https://example.com/api/", "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": {}} ] }
以下是適用於此實例的所有連結,包括在參考的個別「thing」綱要中定義的連結
[ { "contextUri": "https://example.com/api/things", "contextPointer": "", "rel": "self", "targetUri": "https://example.com/api/things", "attachmentPointer": "" }, { "contextUri": "https://example.com/api/things", "contextPointer": "/elements/0", "rel": "self", "targetUri": "https://example.com/api/things/12345", "attachmentPointer": "/elements/0" }, { "contextUri": "https://example.com/api/things", "contextPointer": "/elements/1", "rel": "self", "targetUri": "https://example.com/api/things/67890", "attachmentPointer": "/elements/1" }, { "contextUri": "https://example.com/api/things", "contextPointer": "", "rel": "item", "targetUri": "https://example.com/api/things/12345", "attachmentPointer": "/elements/0" }, { "contextUri": "https://example.com/api/things", "contextPointer": "", "rel": "item", "targetUri": "https://example.com/api/things/67890", "attachmentPointer": "/elements/1" }, { "contextUri": "https://example.com/api/things", "contextPointer": "/elements/0", "rel": "collection", "targetUri": "https://example.com/api/things", "attachmentPointer": "/elements/0" }, { "contextUri": "https://example.com/api/things", "contextPointer": "/elements/1", "rel": "collection", "targetUri": "https://example.com/api/things", "attachmentPointer": "/elements/1" } ]
在所有情況下,都會顯示媒體類型為 application/json 的實例的上下文 URI,該類型不支援片段。如果實例媒體類型是 application/instance+json,它支援 JSON 指標片段,則上下文 URI 將包含與上下文指標欄位相同的片段。對於沒有片段的 application/json 和其他媒體類型,考慮上下文指標以及上下文 URI 至關重要。
有三個「self」連結,一個用於集合,一個用於「elements」陣列中的每個項目。項目「self」連結是在使用「$ref」參考的個別「thing」綱要中定義的。這三個連結可以透過它們的上下文或附加指標來區分。我們將在第 9.5.2 節中重新檢視集合「self」連結的「submissionSchema」。
有兩個「item」連結,一個用於「elements」陣列中的每個項目。與「self」連結不同,這些連結僅在集合綱要中定義。它們每一個都具有與共享相同附加指標的對應「self」連結相同的目標 URI。但是,每個都有不同的上下文指標。「self」連結的上下文是「elements」中的條目,而「item」連結的上下文始終是整個集合,無論特定項目如何。
最後,有兩個「collection」連結,一個用於「elements」中的每個項目。在個別項目綱要中,這些連結會產生以項目資源作為上下文的連結。從集合綱要參考時,上下文是相關「thing」在「elements」陣列中的位置,而不是「thing」自己獨立的資源 URI。
集合連結具有相同的目標 URI,因為只有一個相關的集合 URI。雖然將這兩個連結計算為完整建構連結集合的一部分可能看起來沒有用,但當根據需要建構連結時,此安排表示無論你正在處理哪個「elements」條目,都可以在手邊找到「collection」連結定義。
在這裡,我們將分頁新增到我們的集合。有一個「meta」部分來保存關於目前、下一個和上一個頁面的資訊。大多數綱要與上一節中的相同,並且已被省略。僅完全顯示新的欄位以及新的或(在主要「self」連結的情況下)已變更的連結。
{ "properties": { "elements": { ... }, "meta": { "type": "object", "properties": { "prev": {"$ref": "#/$defs/pagination"}, "current": {"$ref": "#/$defs/pagination"}, "next": {"$ref": "#/$defs/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": "#"} } ], "$defs": { "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://example.com/api/things", "contextPointer": "", "rel": "self", "targetUri": "https://example.com/api/things?offset=0&limit=2", "attachmentPointer": "" }, { "contextUri": "https://example.com/api/things", "contextPointer": "", "rel": "next", "targetUri": "https://example.com/api/things?offset=3&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#/$defs/pagination" }, "submissionSchema": { "$ref": "thing#" }, "targetSchema": { "$ref": "thing-collection#" } }
現在我們看到分頁參數被接受作為輸入,因此我們可以跳轉到集合中的任何頁面。連結關係類型是自訂的,因為一般「collection」連結只能在項目作為其上下文時使用,而不能與入口點或其他資源一起使用。
當我們沒有任何「thing」時,我們就沒有任何具有相關「collection」連結的資源。因此,我們無法使用「collection」連結的提交關鍵字來建立第一個「thing」;必須針對實例評估超結構描述。由於集合實例中的「elements」陣列將為空,因此它也無法向我們提供集合連結。
但是,我們的入口點連結可以將我們帶到空的集合,並且我們可以使用超結構描述中「item」連結的存在來識別它是一個集合。由於「item」連結的上下文是集合,因此我們只需尋找具有相同上下文的「self」連結,然後我們可以將其視為建立操作的集合。
據推測,我們入口點綱要中的自訂連結關係類型足以確保我們找到了正確的集合。可以識別該自訂連結關係類型的客戶端應用程式可能知道它可以立即假設目標是一個集合,但是一般的使用者代理程式無法做到這一點。儘管我們的範例中存在「-collection」後綴,但一般的使用者代理程式無法知道該子字串是指超媒體資源集合還是其他類型的集合。
一旦我們識別出「self」連結適用於正確的集合,我們就可以使用它的「submissionSchema」和/或「submissionMediaType」關鍵字來執行項目建立操作。[CREF8]如果集合未經過篩選和分頁,這會運作良好。但是,一般來說,應該 POST 到將包含已建立資源的集合,並且「self」連結必須包含任何篩選、分頁或其他查詢參數。即使結果項目與篩選不符或未出現在該頁面內,仍然可以 POST 到這樣的「self」連結嗎?有關更多討論,請參閱 GitHub 問題 #421。 [CREF9]Hyper-Schema 的 Draft-04 定義了一個「create」連結關係,其綱要而不是實例作為其上下文。這不符合基於實例的連結模型,並且不正確地使用了連結關係類型的操作名稱。但是,從綱要到集合實例定義設計更正確的連結可能是解決這個問題的一種可能方法。同樣,有關更多詳細資訊,請參閱 GitHub 問題 #421。
JSON Hyper-Schema 定義了 JSON 綱要核心的詞彙,並涉及其中列出的所有安全性考量。作為連結序列化格式,RFC 8288 Web Linking 的安全性考量也適用,並進行了適當的調整(例如,「anchor」作為 LDO 關鍵字而不是 HTTP Link 標頭屬性)。
如第 6.5 節所述,所有描述目標資源的 LDO 關鍵字都是建議性的,且**不得**用來取代目標資源在回應操作時所提供的權威資訊。目標資源的回應**應**指出其自身的超連結綱要 (hyper-schema),該綱要具有權威性。
如果目標回應中的超連結綱要與目前 LDO 所在的超連結綱要(透過 "$id")相符,則目標屬性**可以**被視為具有權威性。[CREF10]需要新增一些關於使用 "$id" 進行欺騙的風險的內容,但鑑於規格的其他部分不鼓勵總是重新下載連結的綱要,因此風險緩解選項尚不清楚。
使用者代理或客戶端應用程式**不得**使用 "targetSchema" 的值來協助解釋在追蹤連結後收到的資料,因為這會使「安全」資料容易被重新解釋。
在選擇如何解釋資料時,伺服器提供的類型資訊(或從檔案名稱推斷的,或任何其他常用方法)**必須**是唯一的考量因素,且連結的 "targetMediaType" 屬性**不得**使用。使用者代理**可以**使用此資訊來決定如何呈現連結或在哪裡顯示連結(例如懸停文字、在新分頁中開啟)。如果使用者代理決定將連結傳遞給外部程式,他們**應**先驗證資料的類型是否通常會傳遞給該外部程式。
這是為了防止重新解釋「安全」資料,類似於對 "targetSchema" 的預防措施。
在 "targetHints" 中傳達的協定中繼資料值**不得**被視為具有權威性。任何可能基於對中繼資料值的不正確假設而適用的協定所定義的安全性考量都適用。
即使沒有直接適用的協定安全性考量,實作也**必須**準備好處理與連結的 "targetHints" 值不符的回應。
當使用 "self" 的連結關係來表示物件的完整表示時,如果目標 URI 與用於請求包含帶有 "self" 連結的目標 URI 的資源表示的 URI 不等效或不是其子路徑,則使用者代理**不應**認為該表示是目標 URI 所表示資源的權威表示。[CREF11]本段中「子路徑」選項的意圖已不再完全清楚。它可能是為了允許集合中嵌入項目的表示使用 "self" 連結,這些表示的目標 URI 通常是該集合 URI 的子路徑,以便被視為具有權威性。然而,這只是一種常見的設計慣例,似乎不是基於 RFC 3986 或任何其他關於 URI 使用的指導。有關進一步討論,請參閱 GitHub issue #485。
感謝 Gary Court、Francis Galiegue、Kris Zyp 和 Geraint Luff 在 JSON 綱要的初始草案中所做的工作。
感謝 Jason Desrosiers、Daniel Perrett、Erik Wilde、Ben Hutton、Evgeny Poberezkin、Brad Bowman、Gowry Sankar、Donald Pipowitch、Dave Finlay 和 Denis Laxalde 對文件的提交和修補。
[json-schema] | Wright, A. 和 H. Andrews, "JSON Schema: A Media Type for Describing JSON Documents", Internet-Draft draft-handrews-json-schema-02, 2017 年 11 月。 |
[json-schema-validation] | Wright, A., Andrews, H. 和 G. Luff, "JSON Schema Validation: A Vocabulary for Structural Validation of JSON", Internet-Draft draft-handrews-json-schema-validation-02, 2017 年 11 月。 |
[relative-json-pointer] | Luff, G. 和 H. Andrews, "Relative JSON Pointers", Internet-Draft draft-handrews-relative-json-pointer-02, 2018 年 1 月。 |
[RFC2119] | Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, 1997 年 3 月。 |
[RFC3986] | Berners-Lee, T., Fielding, R. 和 L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, DOI 10.17487/RFC3986, 2005 年 1 月。 |
[RFC4287] | Nottingham, M. 和 R. Sayre, "The Atom Syndication Format", RFC 4287, DOI 10.17487/RFC4287, 2005 年 12 月。 |
[RFC6570] | Gregorio, J., Fielding, R., Hadley, M., Nottingham, M. 和 D. Orchard, "URI Template", RFC 6570, DOI 10.17487/RFC6570, 2012 年 3 月。 |
[RFC6573] | Amundsen, M., "The Item and Collection Link Relations", RFC 6573, DOI 10.17487/RFC6573, 2012 年 4 月。 |
[RFC6901] | Bryan, P., Zyp, K. 和 M. Nottingham, "JavaScript Object Notation (JSON) Pointer", RFC 6901, DOI 10.17487/RFC6901, 2013 年 4 月。 |
[RFC8288] | Nottingham, M., "Web Linking", RFC 8288, DOI 10.17487/RFC8288, 2017 年 10 月。 |
[I-D.reschke-http-jfv] | Reschke, J., "A JSON Encoding for HTTP Header Field Values", Internet-Draft draft-reschke-http-jfv-06, 2017 年 6 月。 |
[RFC2046] | Freed, N. 和 N. Borenstein, "Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types", RFC 2046, DOI 10.17487/RFC2046, 1996 年 11 月。 |
[RFC4151] | Kindberg, T. 和 S. Hawke, "The 'tag' URI Scheme", RFC 4151, DOI 10.17487/RFC4151, 2005 年 10 月。 |
[RFC5789] | Dusseault, L. 和 J. Snell, "PATCH Method for HTTP", RFC 5789, DOI 10.17487/RFC5789, 2010 年 3 月。 |
[RFC6068] | Duerst, M., Masinter, L. 和 J. Zawinski, "The 'mailto' URI Scheme", RFC 6068, DOI 10.17487/RFC6068, 2010 年 10 月。 |
[RFC7230] | Fielding, R. 和 J. Reschke, "Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing", RFC 7230, DOI 10.17487/RFC7230, 2014 年 6 月。 |
[RFC7231] | Fielding, R. 和 J. Reschke, "Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content", RFC 7231, DOI 10.17487/RFC7231, 2014 年 6 月。 |
[RFC7807] | Nottingham, M. 和 E. Wilde, "Problem Details for HTTP APIs", RFC 7807, DOI 10.17487/RFC7807, 2016 年 3 月。 |
遵循 REST 架構風格約束的超媒體 API 能夠建立通用的使用者代理。此類使用者代理沒有應用程式特定的知識。相反,它理解預先定義的媒體類型、URI 綱要、協定和連結關係,通常透過識別這些內容並協調使用現有軟體來實作對它們的支援。然後,可以在此類使用者代理之上建構客戶端應用程式,重點關注它們自身的語義和邏輯,而不是互動的機制。
超連結綱要一次只關心一個資源和一組相關的連結。就像網頁瀏覽器一次只處理一個 HTML 頁面,而沒有概念該頁面是否或如何作為「網站」的一部分運作一樣,具有超連結綱要感知的使用者代理一次只處理一個資源,而沒有任何概念該資源是否或如何適應 API。
因此,超連結綱要適用於在 API 內使用,但不適用於將 API 描述為獨立完整的實體。沒有辦法在 API 範圍內描述概念,而不是在資源和連結範圍內,並且此類描述超出了 JSON 超連結綱要的範圍。
由於給定的 JSON 超連結綱要在單個時間點與單個資源一起使用,因此它沒有固有的版本控制概念。但是,給定的資源可以隨著時間的推移而變更其使用的綱要或綱要,並且這些綱要的 URI 可以用來指示版本控制資訊。當與支援使用媒體類型參數指示綱要的媒體類型一起使用時,這些帶有版本控制的綱要 URI 可用於內容協商。
資源可以指示它是多個綱要的實例,這允許同時支援多個相容版本。然後,客戶端應用程式可以使用它所識別的超連結綱要,並忽略較新或較舊的版本。
由於超連結綱要一次代表單個資源,因此它不提供對使用連結執行協定操作的所有可能回應的列舉。每個回應(包括錯誤)都被視為其自己的(可能是匿名的)資源,並且應該識別其自身的超連結綱要,並可選擇使用適當的媒體類型,例如 RFC 7807 的「application/problem+json」,以允許使用者代理或客戶端應用程式解釋在協定自身狀態報告之外提供的任何資訊。
可以靜態分析一組超連結綱要,而無需實例資料,以便產生諸如文件或程式碼之類的輸出。但是,如果沒有執行階段實例資料,就無法存取驗證和超連結綱要的完整功能集。
這是一個有意的設計選擇,旨在為超媒體系統提供最大的執行階段靈活性。JSON 綱要作為一種媒體類型,允許為靜態分析和內容產生建立額外的詞彙,本規範中未解決這些詞彙。此外,如果完整設計時描述是一個目標,則個別系統可能會將其使用限制為可以靜態分析的子集。[CREF12]已經提出了用於 API 文件和其他目的的詞彙,歡迎在 https://github.com/json-schema-org/json-schema-vocabularies 上做出貢獻
[CREF13]本節在離開 Internet-Draft 狀態前需移除。