先日るびまに寄稿したハイパーメディア:RailsでWeb APIをつくるには、これが足りないではWeb APIにリンクとフォームが必要だということを強調しました。幸いリンクとしてURLが入ったWeb APIは少しずつ増えてきているように思います。
しかし、リンクでは決まったGETリクエストしかできません。ユーザから入力されたパラメータを付加したGETやPOSTリクエストを行うためにはフォームが必要です。
そこで、フォームをJSONベースのフォーマットで表現するとどうなるか、数少ない現時点での方法を調べました。
- JSON-LD & Schema.org
- JSON-LD & Hydra
- UBER
1. 検索フォーム(GET)
HTML
<form action="http://example.com/search" method="get">
<input type="search" name="q" maxlength="100" required>
<input type="submit">
</form>
GET http://example.com/search?q=the+search
JSON-LD & Schema.org
{
"@context": "http://schema.org/",
"@type": "Thing",
"potentialAction": {
"@type": "SearchAction",
"target": "http://example.com/search?q={q}",
"query-input": "name=q maxlength=100 required"
}
}
Schema.orgではフォームをActionで表現する。アクション元のタイプThing
はSchema.orgのタイプなら何でもよい。
アクションタイプSearchAction
もどんなActionでもOK。
query-inputは以下のオブジェクト表現も可
"query-input": {
"@type": "PropertyValueSpecification",
"valueName": "q",
"valueMaxlength": 100,
"valueRequired": true
}
Google Sitelinks Search Boxでもこれを採用している。
https://developers.google.com/structured-data/slsb-overview
JSON-LD & Hydra
{
"@context": "http://www.w3.org/ns/hydra/core",
"@type": "Collection",
"search": {
"@type": "IriTemplate",
"template": "http://example.com/search?q={q}",
"variableRepresentation": "BasicRepresentation",
"mapping": [
{
"@type": "IriTemplateMapping",
"variable": "q",
"property": "hydra:freetextQuery",
"required": true
}
]
}
}
HydraのCollectionタイプに対してsearchを記述できる。
検索以外のGETフォームの書き方はわからない。
UBER
{
"uber": {
"version": "1.0",
"data": [
{
"name": "search",
"rel": "search",
"url": "http://example.com/search?q={q}",
"templated": "true",
"action": "read"
}
]
}
}
"templated": "true"
にするとurl
がURI Templateとみなされる。
"action": "read"
がGETリクエストを表す。
値の制約などを表すことはできない(仕様に提案されている段階)。
2. レビュー投稿フォーム(POST)
HTML
<form action="http://example.com/movies/123/reviews" method="post">
<input type="text" name="body" maxlength="100" required>
<input type="number" name="rating" min="1" max="5" required>
<input type="submit">
</form>
POST http://example.com/movies/123/reviews
body=yada%2C%20yada%2C%20yada&rating=4
JSON-LD & Schema.org
{
"@context": "http://schema.org/",
"@type": "Thing",
"potentialAction": {
"@type": "ReviewAction",
"target": {
"@type": "EntryPoint",
"urlTemplate": "http://example.com/movies/123/reviews",
"encodingType": "application/x-www-form-urlencoded",
"contentType": "application/ld+json",
"httpMethod": "POST"
},
"object" : "http://example.com/movies/123",
"result": {
"@type": "Review",
"reviewBody-input": "name=body maxlength=100 required",
"reviewRating": {
"ratingValue-input": "name=rating min=1 max=5 required"
}
}
}
}
encodingTypeはリクエストのメディアタイプ、contentTypeがレスポンスのメディアタイプ。
encodingTypeがapplication/x-www-form-urlencodedの場合は明確に定義されていないが、検索の場合から考えてこれで妥当と思われる。
encodingTypeがapplication/ld+jsonの場合は、*-inputのプロパティを含めたActionのJSON-LDをリクエストボディとする。このようなリクエストになる。
POST http://example.com/movies/123/reviews
{
"@context": "http://schema.org/",
"@type": "ReviewAction",
"result": {
"@type": "Review",
"reviewBody": "yada, yada, yada",
"reviewRating": {
"ratingValue": "4"
}
}
}
JSON-LD & Schema.org (Google Email Markup拡張)
{
"@context": "http://schema.org/",
"@type": "EmailMessage",
"potentialAction": {
"@type": "ReviewAction",
"resultReview": {
"@type": "Review",
"reviewRating": {
"@type": "Rating",
"bestRating": "5",
"worstRating": "1"
}
},
"handler": {
"@type": "HttpActionHandler",
"url": "http://example.com/movies/123/reviews",
"requiredProperty": [
{
"@type": "Property",
"name": "resultReview.reviewBody"
},
{
"@type": "Property",
"name": "resultReview.reviewRating.ratingValue"
}
],
"method": "http://schema.org/HttpRequestMethod/POST"
}
}
}
Email Markup用拡張。独自にhandler(HttpActionHandler)を追加している。
https://developers.google.com/schemas/reference/types/HttpActionHandler
JSON-LD & Hydra
拡張形 (augmented operation)
{
"@context": "http://www.w3.org/ns/hydra/core",
"@id": "http://example.com/movies/123/reviews",
"@type": "Resource",
"operation": [
{
"@type": "CreateResourceOperation",
"method": "POST",
"expects": {
"supportedProperty": [
{
"property": "body",
"range": "xsd:string",
"required": true
},
{
"property": "rating",
"range": "xsd:integer",
"required": true
}
]
},
"returns": "Resource"
}
]
}
Resourceタイプに対してoperationを記述する形。ただし対象URLはResource自身(@id
)のみ。
HydraConsoleでは認識されない?
定義形 (supported operation)
{
"@context": "http://www.w3.org/ns/hydra/core",
"@id": "http://example.com/doc#postReview",
"@type": "Link",
"supportedOperation": [
{
"@type": "CreateResourceOperation",
"method": "POST",
"expects": "http://example.com/doc#Review",
"returns": "http://example.com/doc#Review"
}
]
}
ClassもしくはLinkタイプに対してsupportedOperationとして定義する形。この定義をApiDocumentationとして用意しておき、"http://example.com/doc#postReview": "http://example.com/movies/123/reviews"
のように使うと有効になる。
データやcontextとは別のドキュメントとして定義するため、クライアントの実装が少し複雑になる。
UBER
{
"uber": {
"version": "1.0",
"data": [
{
"name": "create",
"url": "http://example.com/movies/123/reviews",
"model": "body={body}&rating={rating}",
"action": "append"
}
]
}
}
model
がリクエストボディのテンプレートになる。
"action": "append"
がPOSTリクエストを表す。
値の条件などを表すことはできない。
参考文献
http://schema.org/docs/actions.html
https://developers.google.com/structured-data/
https://developers.google.com/gmail/markup/
https://developers.google.com/schemas/
http://www.hydra-cg.com/spec/latest/core/
http://rawgit.com/uber-hypermedia/specification/master/uber-hypermedia.html