網際網路工程任務組 | H. Andrews, Ed. |
網際網路草案 | Cloudflare, Inc. |
預定狀態:資訊性 | A. Wright, Ed. |
到期日:2018 年 7 月 23 日 | 2018 年 1 月 19 日 |
JSON 超級綱要:JSON 超媒體註解詞彙表
draft-handrews-json-schema-hyperschema-01
JSON 綱要是一種基於 JSON 的格式,用於使用各種詞彙表來描述 JSON 資料。本文件指定一個詞彙表,用於使用超連結註解 JSON 文件。這些超連結包含描述如何透過 HTTP 等超媒體環境操作和與遠端資源互動的屬性,以及根據實例值判斷連結是否可用的屬性。本文件中描述的超連結序列化格式也可獨立於 JSON 綱要使用。
此草案的議題列表可在此處找到:<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 年 7 月 23 日到期。
著作權 (c) 2018 IETF Trust 和被識別為文件作者的人士。保留所有權利。
本文件受 BCP 78 和 IETF Trust 與 IETF 文件相關的法律條款 (http://trustee.ietf.org/license-info) 的約束,這些條款在本文件發布之日生效。請仔細審閱這些文件,因為它們描述了您對本文件的權利和限制。從本文件中提取的程式碼元件必須包含 Trust 法律條款第 4.e 節中描述的簡化 BSD 授權文字,並在未提供簡化 BSD 授權中所述擔保的情況下提供。
JSON 超級綱要是一個 JSON 綱要詞彙表,用於使用超連結和透過 HTTP 等超媒體環境處理和操作遠端 JSON 資源的說明來註解 JSON 文件。
術語 JSON 超級綱要用於指稱使用這些關鍵字的 JSON 綱要。術語「超級綱要」本身在本規格的範圍內是指 JSON 超級綱要。
引入用於指定連結的主要機制是連結描述物件 (LDO),它是 RFC 8288 第 2 節 [RFC8288]中定義的抽象連結模型的序列化。
本規格將使用 JSON 綱要核心 [json-schema] 和 JSON 綱要驗證 [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/」,則產生的連結的目標 URI 為「https://api.example.com/thing/1234」。
術語「綱要」、「實例」和「後設綱要」應按照 JSON 綱要核心規格 [json-schema] 中的定義進行解釋。
術語「適用」和「附加」應按照 JSON 綱要驗證規格第 3 節 [json-schema-validation] 中的定義進行解釋。
術語「連結」、「連結上下文」(或「上下文」)、「連結目標」(或「目標」) 和「目標屬性」應按照 RFC 8288 第 2 節 [RFC8288] 中的定義進行解釋。
術語「使用者代理程式」應按照 RFC 7230 第 2.1 節 [RFC7230] 中的定義進行解釋,該定義已一般化為適用於超媒體系統中可能使用的任何通訊協定,而不僅僅是 HTTP 用戶端。
本規格定義下列術語
JSON 超級綱要 (Hyper-Schema) 的實作能夠接收超級綱要、實例,以及在某些情況下接收用戶端輸入,並產生一組完全解析的有效連結。如 RFC 8288 第 2 節 [RFC8288] 所定義,連結由上下文(context)、類型化的關係(relation)、目標(target),以及可選的其他目標屬性組成。
關係類型和目標屬性直接取自每個連結的連結描述物件 (Link Description Object)。上下文和目標識別符是從 URI 範本、實例資料,以及(在目標識別符的情況下)用戶端輸入的某種組合建構而成的。
目標始終由 URI 完全識別。由於缺乏用於 application/json 和許多其他可以與 JSON 超級綱要一起使用的媒體類型的 URI 片段識別符語法,因此上下文可能僅由 URI 部分識別。在這種情況下,剩餘的識別將以 JSON 指標 (JSON Pointer) 的形式提供。
一些 IANA 註冊的連結關係類型在 JSON 超級綱要文件中具有特定的語義。"self" 連結用於與實例文檔所表示的資源進行互動,而 "collection" 和 "item" 連結則識別可以假設為集合特定語義的資源。
JSON 超級綱要元綱當前 URI 為 <https://json-schema.dev.org.tw/draft-07/hyper-schema#>。
連結描述格式 [ldo] 可以在沒有 JSON 綱要的情況下使用,並且可以透過參考規範的連結描述綱要作為使用連結的資料結構的綱要來宣告此格式的使用。規範的連結描述綱要的 URI 為:<https://json-schema.dev.org.tw/draft-07/links#>。
JSON 超級綱要實作可以自由地以任何格式提供輸出。但是,定義了一種特定格式,用於一致性測試套件,該格式也用於說明「實作要求」 [implementation]中的要點,以及顯示 範例 [examples]產生的輸出。建議實作能夠產生這種格式的輸出以方便測試。描述建議輸出格式的 JSON 綱要的 URI 為 <https://json-schema.dev.org.tw/draft-07/hyper-schema-output#>。
適用於實例中某個位置的所有綱要中的超級綱要關鍵字,如 JSON 綱要驗證第 3 節 [json-schema-validation] 所定義,可以與該實例一起使用。
當多個子綱要適用於給定的子實例時,所有 "link" 陣列必須以任意順序合併為單一集合。結果集合中的每個物件必須保留其自己的適用 "base" 值列表,這些值按照解析順序來自同一綱要和任何父綱要。
與所有 JSON 綱要關鍵字一樣,本節中描述的所有關鍵字都是可選的。最小的有效 JSON 超級綱要是空白物件。
如果存在此關鍵字,則必須首先解析為 URI 範本 [uriTemplating],然後必須根據實例的當前 URI 基準解析為 URI 參考。在處理包含 "base" 的子綱要及其中的所有子綱要時,結果必須設定為實例的新 URI 基準。
當解析 "base" 範本以用於 "anchor" 時,其解析過程可能與用於 "href" 時不同,這在 URI 範本部分中有詳細說明。
綱要的 "links" 屬性用於將連結描述物件與實例關聯。此屬性的值必須是陣列,並且陣列中的項目必須是連結描述物件,如下定義。
連結描述物件 (LDO) 是 RFC 8288 第 2 節 [RFC8288] 中定義的抽象連結模型的序列化。如該文件中所述,連結由上下文、關係類型、目標以及可選的目標屬性組成。JSON 超級綱要的 LDO 提供了所有這些,以及使用 JSON 綱要以各種方式描述連結輸入的其他功能。
由於使用 URI 範本來識別連結上下文和目標,以及在識別目標時可選地進一步使用用戶端輸入,因此 LDO 是一個連結範本,當與 JSON 實例文檔一起使用時,可能會解析為多個連結。
LDO 的特定用途,通常涉及跨協定的請求和回應,被稱為操作。對於許多協定,任何給定連結上都可能有多個操作。協定由目標的 URI 方案指示。請注意,並非所有 URI 方案都指示可用於通訊的協定,即使具有指示此類協定的 URI 方案的資源也不一定能透過該協定使用。
連結描述物件必須是一個物件,並且必須存在 "href" [href] 和 "rel" [rel] 屬性。本節將簡要介紹每個關鍵字,並在本文檔的後續部分提供額外的使用說明和全面的範例。
在 JSON 超級綱要中,連結的上下文資源預設為連結所附加到的子實例(如 JSON 綱要驗證規範的第 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] 必須根據實例的基準 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 節中定義的單一連結關係類型。
此屬性是必要的。
根據 RFC 4287 的 4.2.7.2 節 [RFC4287] 的原始定義,「self」連結表示目標 URI 指向與連結上下文等效的資源。在 JSON Hyper-Schema 中,「self」連結必須可從實例解析,因此不得出現「hrefSchema」。
Hyper-schema 作者應使用「templateRequired」來確保「self」連結具有所有使用所需的實例資料。
Hyper-schema 實作必須識別關係類型為「self」的連結,當該連結以整個目前的實例文件作為其上下文時,描述使用者代理程式如何與該實例文件所表示的資源互動。
RFC 6573 [RFC6573] 定義並註冊了「item」和「collection」連結關係類型。JSON Hyper-Schema 對這些類型所指示的集合資源施加了額外的語義。
實作必須將「collection」連結的目標和「item」連結的上下文識別為集合。
超媒體中一個眾所周知的設計模式是使用集合資源來建立集合的成員,並為其提供伺服器指派的 URI。如果 URI 方案所指示的協定定義了適用於建立具有伺服器指派 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]。
此屬性為必要屬性。
本節中的關鍵字用於解析 hyper-schema 中涉及的所有 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」。
此屬性提供一個 schema,預期會描述連結目標的表示。根據協定,schema 可能會也可能不會描述對連結執行的任何特定操作的請求或回應。請參閱 JSON Hyper-Schema 和 HTTP [HTTP] 節,以深入討論如何將此關鍵字用於 HTTP。
此屬性的值僅為建議性質。它表示預期可透過與目標資源互動來發現的資訊,通常以協定特定控制資訊或中繼資料的形式,例如回應 HTTP HEAD 或 OPTIONS 請求時傳回的標頭。協定由「href」URI 方案決定,但請注意,無法保證資源可透過此類協定存取。
此屬性的值應為物件。此物件的索引鍵應為控制資料欄位名稱的小寫形式。每個值都應為陣列,以便統一處理多值欄位。多個值必須以陣列形式呈現,而不是單一字串。
具有不適合以 JSON 物件表示的控制資訊的協定可以由另一種資料類型(例如陣列)表示。
JSON Hyper-Schema 實作必須忽略無法理解為所指示協定一部分的值。應用程式可以使用此類值,但不得假設與其他實作之間的互通性。
實作不得假設所有可探索到的資訊都在此物件中說明。用戶端應用程式必須正確處理與此屬性值衝突的執行階段回應。
用戶端應用程式不得假設實作會根據此屬性的值自動採取任何動作。
有關將此關鍵字用於 HTTP 和類似協定的指引,請參閱 「JSON Hyper-Schema 和 HTTP」 [HTTP]。
有四種方式可以使用連結的用戶端輸入,每種方式都由單獨的連結描述物件關鍵字處理。執行操作時,使用者代理程式應忽略與其語義不相關的 schema。
「hrefSchema」連結描述屬性的值必須為有效的 JSON Schema。此 schema 用於驗證用戶端輸入或其他使用者代理程式資料,以填寫 「href」 [href] 中的 URI 範本。
省略「hrefSchema」或將整個 schema 設定為「false」會阻止接受任何使用者代理程式資料。
將適用於特定變數的任何子 schema 設定為 JSON 字面值「false」會阻止接受該單一變數的任何使用者代理程式資料。
對於可從實例資料解析的範本變數,如果實例資料針對 "hrefSchema" 中所有適用的子模式皆有效,則必須使用該資料預先填入該變數的輸入資料集。
請注意,即使資料是從實例預先填入,"hrefSchema" 中該變數的驗證模式也不必與適用於實例資料位置的驗證模式相同。這允許使用者代理資料使用不同的驗證規則,例如支援日期時間輸入的文字月份,但儲存時使用標準日期時間格式。
在接受輸入(可能會覆寫預先填入的實例資料)後,產生的資料集必須成功地根據 "hrefSchema" 的值進行驗證。如果驗證失敗,則不得使用該連結。如果驗證成功,則「URI 範本化」章節中給定的流程將繼續使用此更新的資料集。
[CREF2]與 "targetHints" 類似,此關鍵字的規範有些不足,旨在鼓勵實驗和回饋,以便在彈性和清晰度之間取得平衡。
如果存在,此屬性是協定特定的請求標頭或類似的控制和元資料的模式。此物件的值必須是有效的 JSON Schema。協定由 "href" URI 方案決定,但請注意,無法保證可透過此協定存取資源。此模式僅供參考;目標資源的行為不受其存在約束。
此關鍵字的目的在於宣傳目標資源的互動功能,並向使用者代理和用戶端應用程式指示哪些標頭和標頭值可能有用。使用者代理和用戶端應用程式可以使用此模式驗證相關標頭,但不得假設缺少標頭或值是被禁止使用的。雖然模式作者可以將 "additionalProperties" 設定為 false,但**不建議**這樣做,並且**不得**阻止用戶端應用程式或使用者代理在發出請求時提供額外的標頭。
JSON 資料模型到標頭的確切對應方式取決於協定。但是,在大多數情況下,此模式應指定 "object" 的類型,而屬性名稱應為控制資料欄位名稱的小寫形式。請參閱 「JSON Hyper-Schema 和 HTTP」 [HTTP] 章節,以取得使用此關鍵字處理 HTTP 和類似協定的詳細指南。
"headerSchema" 適用於協定支援的任何請求方法或命令。在產生請求時,使用者代理和用戶端應用程式**應忽略**與該請求無關的標頭模式。
在 JSON Hyper-Schema 中,"targetSchema" [targetSchema] 提供目標資源表示法的非權威性描述。用戶端應用程式可以使用 "targetSchema" 來建構用於取代或修改表示法的輸入,或作為基於修補媒體類型建立修補文件的基本表示法。
或者,如果缺少 "targetSchema" 或用戶端應用程式偏好僅使用權威性資訊,則它可以與目標資源互動以確認或探索其表示法結構。
"targetSchema" 並不旨在描述連結操作的回應,除非回應語義表示它是目標資源的表示法。在所有情況下,回應本身指示的模式都具有權威性。請參閱 「JSON Hyper-Schema 和 HTTP」 [HTTP] 以取得詳細範例。
"submissionSchema" [submissionSchema] 和 "submissionMediaType" [submissionMediaType] 關鍵字描述目標資源實作的處理函數的網域。否則,如上所述,對於不相關的操作,將忽略提交模式和媒體類型。
如果存在,此屬性表示用戶端應用程式和使用者代理應為 "submissionSchema" [submissionSchema] 描述的請求酬載使用的媒體類型格式。
省略此關鍵字的行為與值為 application/json 的行為相同。
請注意,"submissionMediaType" 和 "submissionSchema" 不限於 HTTP URI。 [CREF3]此陳述可能移動到範例結束的地方。
此屬性包含一個模式,該模式定義要根據 "submissionMediaType" 屬性編碼並傳送到目標資源進行處理的文件的可接受結構。這可以視為描述目標資源實作的處理函數的網域。
這與 "targetSchema" [targetSchema] 屬性是不同的概念,後者描述目標資訊資源(包括在 PUT 請求中替換資源內容),而 "submissionSchema" 描述使用者提交的請求資料,以供資源評估。"submissionSchema" 旨在用於具有不一定以目標表示法定義的酬載的請求。
省略 "submissionSchema" 的行為與值為 "true" 的行為相同。
在較高的層次上,符合規範的實作將滿足以下要求。這些要求中的每一項都在個別的關鍵字章節和關鍵字群組概觀中進行了更詳細的介紹。
請注意,關於實作**必須**如何識別 "self"、"collection" 和 "item" 連結的要求已在 連結關係類型 [relationType] 章節中充分說明,此處不再重複。
雖然它不是實作的強制格式,但測試套件中使用的輸出格式總結了每個連結在使用前需要計算的內容
未參與產生上述資訊的其他 LDO 關鍵字會以與測試套件產生輸出時完全相同的形式包含。除非特別相關,否則這裡不會進一步討論這些欄位。
在使用連結之前,必須將超模式應用於實例並尋找所有適用且有效的連結來探索連結。請注意,除了收集有效的連結外,任何解析每個 LDO 的 URI 範本所需的 "base" [base] 值也必須被找到,並透過對實作的 URI 範本解析流程最有用的任何機制與 LDO 相關聯。
實作**必須**支援依其附加指標或內容指標查詢連結,方法是執行查詢,或提供具有兩個指標都已確定的所有連結的集合,以便使用者代理可以自行實作查詢。
當依內容指標執行查詢時,**必須**以連結所附加的相同陣列元素的順序傳回附加至相同陣列元素的連結。
三個超模式關鍵字是 URI 範本 [RFC6570]:"base"、"anchor" 和 "href"。每個關鍵字都單獨解析為 URI 參考,然後將錨點或 href URI 參考針對 base 進行解析(base 本身會根據需要針對先前的 base 進行解析,每個 base 首先從 URI 範本解析為 URI 參考)。
所有三個關鍵字都共用相同的演算法,用於從實例資料解析變數,該演算法使用 "templatePointers" 和 "templateRequired" 關鍵字。在解析 "href" 時,會修改該演算法,以便根據 "hrefSchema" 關鍵字選擇性地接受使用者輸入,並且解析所需的任何 "base" 範本以轉換為絕對 URI。
對於每個 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: 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 之間的差異),因此應使用任何合理的表示形式。架構和 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,第 4.3.1 節 - "GET",第 4.3.4 節 "PUT" 和第 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*」關鍵字(參見 第 9.3 節 中非 HTTP 的範例)之外,「Accept-Post」標頭可用於指定必要的媒體類型,並且可以透過「targetHints」欄位來宣告。[CREF4]如果兩者都使用會發生什麼?此外,「submissionSchema」是必須支援的,而「targetHints」最多是應該支援的。但在「targetHints」中禁止使用「Accept-Post」似乎不正確。
對 POST 的成功回應,除了設定了「Content-Location」的 201 或 200 之外,同樣沒有 HTTP 定義的語意。與所有 HTTP 回應一樣,回應中的任何表示都應該連結到它自己的超綱要(hyper-schema),以表明如何處理它。如 附錄 A.2 中所述,將超連結與所有可能的操作回應連接起來,不屬於 JSON 超綱要的範圍。
HTTP 回應標頭資訊的 JSON 序列化應該遵循正在開發中的 「HTTP 標頭欄位值的 JSON 編碼」[I-D.reschke-http-jfv] 所建立的指南。該文件中範例所示的方法應該盡可能地應用於其他類似結構的標頭。
所有可能的 HTTP 方法回應的標頭都共用「headerSchema」。特別是,HEAD 回應中出現的標頭和 OPTIONS 回應中出現的標頭都可以出現。「headerSchema」內部不會區分哪個方法回應包含哪個標頭。
建議綱要作者在適用時,提供以下類型 HTTP 標頭值的提示:
一般來說,在不同時間可能具有不同值的標頭不應包含在「targetHints」中。
綱要應該編寫成描述遵循正在開發中的 「HTTP 標頭欄位值的 JSON 編碼」[I-D.reschke-http-jfv] 所建立指南的 JSON 序列化。該文件中範例所示的方法應該盡可能地應用於其他類似結構的標頭。
建議綱要作者在適用時,描述以下類型 HTTP 標頭的可用用法:
快取控制和條件請求標頭等標頭通常由中介而非資源實作,因此通常不適用於描述。雖然資源必須提供使用條件請求所需的信息,但此類標頭和相關回應的運行時處理並非資源特定的。
當使用 HTTP 或類似 HTTP 的協定(如 CoAP)時,這是透過將要建立的個別資源的表示 POST 到集合資源來完成的。識別集合和項目資源的過程在 第 6.2.3 節 中描述。
JSON 超綱要促進 HTTP 內容協商,並允許主動和被動策略的混合。如上所述,超綱要可以包含 HTTP 標頭(例如「Accept」、「Accept-Charset」、「Accept-Language」等)的綱要,並使用「headerSchema」關鍵字。使用者代理或客戶端應用程式可以使用此綱要中的資訊(例如支援語言的列舉列表)來代替發出初始請求以啟動被動協商過程。
這樣,設定這些標頭的主動內容協商技術可以從伺服器關於可能值的資訊中獲取,類似於檢查被動協商中的替代方案列表。
對於允許將綱要指定為媒體類型參數的媒體類型,「Accept」請求中傳送或在「headerSchema」中宣告的值可以包含預期協商的表示形式符合的綱要 URI。內容協商中綱要參數的一種可能用途是,如果資源隨著時間的推移符合多個不同的綱要版本。客戶端應用程式可以透過這種方式在「Accept」標頭中指示它理解哪些版本。
本節說明如何使用建構 URI 和 JSON 指標的關鍵字。結果以測試套件使用的格式顯示。[CREF6]需要發布並連結它,但對於此階段正在審查事物的人來說,它應該是不言自明的。
大多數其他關鍵字不是很簡單(「title」和「description」),就是將驗證應用於特定類型的輸入、請求或回應,或是具有協定特定的行為。示範 HTTP 用法的範例可在 附錄 中找到[HTTP]。
在此範例中,我們將假設一個範例 API,其入口點 URI 為 https://example.com,這是一個空的 JSON 物件,並帶有指向綱要的連結。在這裡,入口點本身沒有任何資料,只存在為了提供一組初始連結
GET https://api.example.com HTTP/1.1 200 OK Content-Type: application/json Link: <https://schema.example.com/entry> rel=describedBy {}
連結的超綱要定義了 API 的基本 URI,並提供了兩個連結:一個指向 API 文件說明的「about」連結,以及一個表明這是基本 URI 綱要的「self」連結。在這種情況下,基本 URI 也是入口點 URI。
{ "$id": "https://schema.example.com/entry", "$schema": "https://json-schema.dev.org.tw/draft-07/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/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」,但自連結需要它。這是合理的:「事物」只有在建立並且伺服器已指派 ID 時才具有 URI。但是,您可以將此綱要與僅包含資料欄位的實例一起使用,這允許您驗證您即將建立的「事物」實例。
讓我們新增一個連結到我們的入口點綱要,如果您可以將其 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 超綱要可以描述同一連結上相同操作的這兩種輸入。
「submissionSchema」和「submissionMediaType」欄位用於描述不是目標資源表示的有效負載。與「http(s)://」URI 一起使用時,它們通常是指 POST 請求有效負載,如關於 HTTP 用法的附錄中所見[HTTP]。
在這種情況下,我們使用「mailto:」URI,根據 RFC 6068,第 3 節 [RFC6068],它不提供任何檢索資源的操作。它只能用於建構要傳送的訊息。由於沒有可檢索、可替換或可刪除的目標資源的概念,因此不使用「targetSchema」和「targetMediaType」。非表示有效負載由「submissionSchema」和「submissionMediaType」描述。
我們使用「submissionMediaType」來指示多部分/替代的有效負載格式,提供相同資料的兩種表示(HTML 和純文字)。由於多部分/替代訊息是有序序列(最後一部分是最偏好的替代方案),因此我們在「submissionSchema」中將該序列建模為陣列。由於每個部分本身都是具有媒體類型的文件,因此我們將陣列中的每個項目建模為字串,並使用「contentMediaType」來指示字串內的格式。
請注意,諸如 multipart/form-data 之類的媒體類型(將名稱與每個部分關聯且不是有序的)應建模為 JSON 物件而不是陣列。
請注意,某些行為了適應此文件的寬度限制而換行。
{ "$id": "https://schema.example.com/interesting-stuff", "$schema": "https://json-schema.dev.org.tw/draft-07/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": "" }
請注意,這裡使用 "href*" 關鍵字來代替 "targetUri"。這些是三種可能的 "targetUri" 值,涵蓋不同類型的輸入。以下是每個的範例:
連結是從上下文資源到目標資源的類型化連線。較舊的連結序列化支援一個 "rev" 關鍵字,它會採用與 "rel" 相同的連結關係類型,但會反轉語意。此功能早已被棄用,因此 JSON Hyper-Schema 不支援它。相反地,可以使用 "anchor" 更改上下文 URI 的功能來反轉連結的方向。它也可以用來描述兩個資源之間的連結,這兩個資源都不是目前的資源。
例如,有一個 IANA 註冊的 "up" 關係,但沒有 "down"。在 HTTP 連結標頭中,您可以將 "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" 連結時,工具可以輕易偵測到這種反向連結。
以下超媒體綱要套用至上述回應中的實例,將產生相同的 "self" 連結和帶有 "anchor" 的 "up" 連結。它也展示了如何使用範本化的 "base" URI,以及 "templatePointers" 中的絕對和相對 JSON 指標。
{ "$id": "https://schema.example.com/tree-node", "$schema": "https://json-schema.dev.org.tw/draft-07/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-07/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" 是透過此連結建立 "thing" 的綱要。
現在我們要描述 "thing" 的集合。此綱要描述一個集合,其中每個項目的表示都與個別 "thing" 的表示相同。雖然許多集合表示僅包含項目表示的子集,但此範例使用全部表示來盡可能減少綱要的數量。實際的集合項目會以陣列形式出現在物件中,因為我們會在下一個範例中將更多欄位新增至該物件。
{ "$id": "https://schema.example.com/thing-collection", "$schema": "https://json-schema.dev.org.tw/draft-07/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": {}} ] }
以下是適用於此實例的所有連結,包括在參考的個別 "thing" 綱要中定義的連結
[ { "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" } ]
在所有情況下,都會顯示媒體類型為 application/json 的實例的上下文 URI,該類型不支援片段。如果實例媒體類型為 application/instance+json,該類型支援 JSON 指標片段,則上下文 URI 將包含與上下文指標欄位相同的片段。對於 application/json 和其他沒有片段的媒體類型,考量上下文指標以及上下文 URI 至關重要。
有三個 "self" 連結,一個用於集合,一個用於 "elements" 陣列中的每個項目。項目 "self" 連結是在個別 "thing" 綱要中定義的,該綱要使用 "$ref" 參考。這三個連結可以透過它們的上下文或附加指標來區分。我們將在 第 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": "#/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" 連結只能與項目作為其上下文一起使用,而不能與進入點或其他資源一起使用。
當我們沒有任何 "thing" 時,我們沒有任何具有相關 "collection" 連結的資源。因此,我們無法使用 "collection" 連結的提交關鍵字來建立第一個 "thing";必須根據實例評估超媒體綱要。由於集合實例中的 "elements" 陣列將為空,因此它也無法向我們提供集合連結。
但是,我們的進入點連結可以將我們帶到空的集合,並且我們可以利用超媒體綱要中 "item" 連結的存在來識別它是一個集合。由於 "item" 連結的上下文是集合,因此我們只需尋找具有相同上下文的 "self" 連結,然後為了建立操作的目的,我們可以將其視為集合。
據推測,我們進入點綱要中的自訂連結關係類型足以確保我們找到了正確的集合。識別該自訂連結關係類型的用戶端應用程式可能會知道它可以立即假設目標是一個集合,但是一般使用者代理無法這樣做。儘管我們的範例中存在 "-collection" 後綴,但是一般使用者代理將無法知道該子字串是否表示超媒體資源集合或其他類型的集合。
一旦我們將 "self" 連結識別為用於正確的集合,我們就可以使用其 "submissionSchema" 和/或 "submissionMediaType" 關鍵字來執行項目建立操作。[CREF8]如果集合未經篩選且未分頁,則此操作效果良好。但是,通常應該將 POST 發送到將包含建立資源的集合,並且 "self" 連結必須包含任何篩選器、分頁或其他查詢參數。即使產生的項目與篩選器不符或沒有出現在該頁面中,將 POST 發送到這樣的 "self" 連結是否仍然有效?請參閱 GitHub issue #421 以瞭解更多討論。 [CREF9]Hyper-Schema 的草案 04 定義了一個 "create" 連結關係,其綱要 (而非實例) 作為其上下文。這不符合基於實例的連結模型,並且錯誤地使用操作名稱作為連結關係類型。但是,定義從綱要到集合實例的更正確設計的連結可能是解決此問題的一種可能方法。同樣,請參閱 GitHub issue #421 以瞭解更多詳細資訊。
JSON Hyper-Schema 為 JSON Schema 核心定義了詞彙表,並考量其中列出的所有安全性考量。作為連結序列化格式,RFC 8288 Web Linking [RFC8288] 的安全性考量也適用,並進行適當調整 (例如,"anchor" 作為 LDO 關鍵字,而不是 HTTP 連結標頭屬性)。
如 第 6.5 節中所述,所有描述目標資源的 LDO 關鍵字都是建議性的,不得用來代替目標資源在回應操作時提供的權威資訊。目標資源回應應指出它們自己的超媒體綱要,該綱要是權威的。
如果目標回應中的超媒體綱要與找到目前 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 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., "網頁連結", RFC 8288, DOI 10.17487/RFC8288, 2017 年 10 月。 |
[relative-json-pointer] | Luff, G. 和 H. Andrews, "相對 JSON 指標", Internet-Draft draft-handrews-relative-json-pointer-01, 2018 年 1 月。 |
[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 方案、協議和連結關係。然後,可以在此類使用者代理之上構建客戶端應用程式,將重點放在它們自己的語義和邏輯,而不是互動的機制上。
超架構一次只關心一個資源和一組關聯的連結。正如網頁瀏覽器一次只處理一個 HTML 頁面,而沒有概念該頁面是否或如何作為「網站」的一部分運作一樣,具有超架構意識的使用者代理一次只處理一個資源,而沒有任何概念該資源是否或如何適應 API 中。
因此,超架構適合在 API 中使用,但不適合將 API 作為完整的實體進行描述。沒有辦法描述 API 範圍的概念,而不是資源和連結範圍的概念,並且此類描述超出 JSON 超架構的範圍。
由於給定的 JSON 超架構在單一時間點與單一資源一起使用,因此它沒有固有的版本控制概念。但是,給定的資源可以隨著時間的推移而更改它使用的架構或多個架構,並且這些架構的 URI 可以用於指示版本資訊。當與支援使用媒體類型參數指示架構的媒體類型一起使用時,這些版本化的架構 URI 可用於內容協商。
資源可以指示它是多個架構的實例,這允許同時支援多個相容版本。然後,客戶端應用程式可以使用它所識別的超架構,並忽略較新或較舊的版本。
由於超架構一次表示單一資源,因此它不提供對使用連結執行的協定操作的所有可能回應的列舉。每個回應,包括錯誤,都被視為它自己的(可能是匿名的)資源,並且應該識別它自己的超架構,並且可以選擇使用適當的媒體類型,例如 RFC 7807 的 "application/problem+json" [RFC7807],以允許使用者代理或客戶端應用程式解釋超出協定本身狀態報告提供的任何資訊。
可以在沒有實例資料的情況下靜態分析一組超架構,以便產生諸如文件或程式碼之類的輸出。但是,如果沒有執行時實例資料,就無法存取驗證和超架構的完整功能集。
這是一種有意的設計選擇,旨在為超媒體系統提供最大的執行時彈性。作為媒體類型的 JSON Schema 允許建立用於靜態分析和內容產生的額外詞彙,而本規範中並未提及這些詞彙。此外,如果完整設計時描述是目標,則個別系統可能會將其使用限制為可以靜態分析的子集。[CREF12]已提出用於 API 文件和其他目的的詞彙,歡迎在 https://github.com/json-schema-org/json-schema-vocabularies 貢獻
[CREF13]此節將在離開 Internet-Draft 狀態之前移除。