LoginSignup
19
20

More than 5 years have passed since last update.

Phoenix で JSON Schema を使って Validation する

Posted at

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 解釈用)というライブラリを使いますので、こちらを依存関係に追加します。

mix.exs
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 を作成しましょう。

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 を以下のように改修しましょう。

router.exs
defmodule JsonSchemaSample.Router do
  ...
  scope "/", JsonSchemaSample do
    # :brower -> :api に変更
    pipe_through :api

    # get -> post に変更
    post "/", PageController, :index
  end
  ...
end

コントローラを実装する

さて、いよいよコントローラの実装です。
POST された JSON が妥当かどうかを返却させてみます。

page_controller.ex
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 してみましょう。
まずは正常パターンから

data1.json
{
  "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 ナシ版も送ってみましょう。

data2.json
{
  "name": "johnny knoxville",
  "birthdate": "1971/03/11"
}
$ curl -H "Content-type: application/json" -X POST -d @data2.json "localhost:4000/"
true

これも大丈夫そうです。
次に異常パターンとして、name に数値を入れてみます。

data3.json
{
  "name": 12345,
  "birthdate": "1971/03/11"
}
$ curl -H "Content-type: application/json" -X POST -d @data3.json "localhost:4000/"
false

異常が検知されました。
最後に birthdate の許容しないパターンにしてみます。

data4.json
{
  "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時点)開発されているようなので、今後に期待
    • 開発の手伝いをしても良さそう
19
20
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
19
20