JSON ベースの API では JSON Schema を使った Validation が非常に有効です。
Elixir で JSON Schema を使った JSON の妥当性検証をしてみます。
Phoenix アプリケーションをセットアップする
json_schema_sample
というアプリケーションを作ります。
$ mix phoenix.new json_schema_sample
今回は poison(JSON デコード用)と ex_json_schema(JSON Schema 解釈用)というライブラリを使いますので、こちらを依存関係に追加します。
defmodule JsonSchemaSample.Mixfile do
...
defp deps do
[{:phoenix, "~> 1.0.2"},
{:phoenix_ecto, "~> 1.1"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.1"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:cowboy, "~> 1.0"},
# poison を追加
{:poison, "~> 1.5"},
# ex_json_schema を追加
{:ex_json_schema, "~> 0.2.0"}]
end
end
最後に依存関係をダウンロードして完了です。
$ mix deps.get
JSON Schema を用意する
人物の名称、生年月日、身長を受け取る API を想定してみます。
- 名称 name
- 文字列
- 生年月日 birthdate
- xxxx/xx/xx 形式の文字列(x は数字)
- 2015/99/99 のようなありえない年月日もひとまず許容
- 身長 height
- 整数値
- 未指定も許容
以上のルールを元に、sample_schema.json
を作成しましょう。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "/",
"type": "object",
"properties": {
"name": {
"id": "name",
"type": "string"
},
"birthdate": {
"id": "birthdate",
"type": "string",
"pattern": "^[0-9]{4}/[0-9]{2}/[0-9]{2}$"
},
"height": {
"id": "height",
"type": "integer"
}
},
"required": [
"name",
"birthdate"
]
}
ちなみに、オリジナルの JSON Schema を作るときは jsonschema.net のようなジェネレータを使うとラクです。
上記のファイルを priv/schema/
に配置して完了です。
ルーティングを設定する
JSON データを POST で受け取る API を作ります。
router.exs
を以下のように改修しましょう。
defmodule JsonSchemaSample.Router do
...
scope "/", JsonSchemaSample do
# :brower -> :api に変更
pipe_through :api
# get -> post に変更
post "/", PageController, :index
end
...
end
コントローラを実装する
さて、いよいよコントローラの実装です。
POST された JSON が妥当かどうかを返却させてみます。
defmodule JsonSchemaSample.PageController do
use JsonSchemaSample.Web, :controller
def index(conn, params) do
schema = Poison.decode!(File.read! "priv/schema/sample_schema.json")
|> ExJsonSchema.Schema.resolve
json conn, ExJsonSchema.Validator.valid?(schema, params)
end
end
sample_schema.json
を読み込んで Poison でデコードし、それを ExJsonSchema.Schema.resolve/1
に渡しています。
これにより解釈されスキーマデータが取得できます。
あとは、ExJsonSchema.Validator.valid?/2
で、取得したスキーマで入力された JSON(マップ)を検証します。
この関数は、妥当であれば true, 妥当でなければ false を返します。
ExJsonSchema がいかにシンプルに使えるかが、とても良くわかると思います。
なお、Phenix では Content-type: application/json
で JSON データを送ると、自動で params
に値をマッピングしてくれます。
ちなみに、Content-type: plain/text
でデータを送ると params
には何もマッピングされません。この場合は Plug.Conn.read_body/1
にコネクション情報(conn)を渡すことで、テキストデータを読み取れます。
動作を確認してみる
curl を使って JSON データを POST してみましょう。
まずは正常パターンから
{
"name": "johnny knoxville",
"birthdate": "1971/03/11",
"height": 185
}
$ curl -H "Content-type: application/json" -X POST -d @data1.json "localhost:4000/"
true
ちゃんと ture が返ってきました。
height ナシ版も送ってみましょう。
{
"name": "johnny knoxville",
"birthdate": "1971/03/11"
}
$ curl -H "Content-type: application/json" -X POST -d @data2.json "localhost:4000/"
true
これも大丈夫そうです。
次に異常パターンとして、name に数値を入れてみます。
{
"name": 12345,
"birthdate": "1971/03/11"
}
$ curl -H "Content-type: application/json" -X POST -d @data3.json "localhost:4000/"
false
異常が検知されました。
最後に birthdate の許容しないパターンにしてみます。
{
"name": "johnny knoxville",
"birthdate": "1971/aa/11"
}
$ curl -H "Content-type: application/json" -X POST -d @data4.json "localhost:4000/"
false
ちゃんと正規表現も効きました。
感想
- ExJsonSchema は非常にシンプルに使える
- 追加・更新処理を JSON でやり取りするような API を作る場合に有効活用できそう
- ExJsonSchema はここ最近(2015/09/30時点)開発されているようなので、今後に期待
- 開発の手伝いをしても良さそう