LoginSignup
22
23

More than 5 years have passed since last update.

JSONでフォームを表現する

Last updated at Posted at 2015-06-13

先日るびまに寄稿したハイパーメディア:RailsでWeb APIをつくるには、これが足りないではWeb APIにリンクとフォームが必要だということを強調しました。幸いリンクとしてURLが入ったWeb APIは少しずつ増えてきているように思います。

しかし、リンクでは決まったGETリクエストしかできません。ユーザから入力されたパラメータを付加したGETやPOSTリクエストを行うためにはフォームが必要です。

そこで、フォームをJSONベースのフォーマットで表現するとどうなるか、数少ない現時点での方法を調べました。

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

22
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
23