fukuoka.ex代表のpiacereです
ご覧いただいて、ありがとうございます
前回に引き続き、mix phx.gen.jsonで自動生成したAPIのリネームをしたいとき、どこを直す必要があり、直さなくて良いところがどこか、イマイチ分かりにくいので、まとめてみました
後半は、「リクエストJSON」と「レスポンスJSON」のリネームです
なお、「Phoenix」は、ElixirのWebフレームワークです
内容が、面白かったり、役に立ったら、「いいね」よろしくお願いします
本コラムの検証環境、事前構築のコマンド
本コラムは、以下環境で検証しています(Windowsで実施していますが、Linuxやmacでも動作する想定です)
- Windows 10
- Elixir 1.8.1 ※最新版のインストール手順はコチラ
- Phoenix 1.4.0 ※最新版のインストール手順はコチラ
- PostgreSQL 10.1 ※最新版のインストール手順はコチラ
また、以下コマンドでPhoenix PJ+JSON APIを構築しています
mix phx.new api_sample --no-webpack
cd api_sample
mix ecto.create
mix phx.gen.json Api Post post title:string body:string
mix ecto.migrate
【手動でルーティング追加とprotect_from_forgery無効化をする】
iex -S mix phx.server
リクエストJSONのキーを変更するとき
「POST」と「PUT」が、リクエストJSONのキーを要求するので、コントローラのcreate()とupdate()を変更する必要があります
いずれも、関数パターンマッチで実装されているので、マッチするパターンを変更するだけです
defmodule ApiSampleWeb.PostController do
…
def create(conn, %{"hoge" => post_params}) do
# modify here------^
with {:ok, %Post{} = post} <- Api.create_post(post_params) do
conn
|> put_status(:created)
|> put_resp_header("location", Routes.post_path(conn, :show, post))
|> render("show.json", post: post)
end
end
…
def update(conn, %{"id" => id, "hoge" => post_params}) do
# modify here------------------^
post = Api.get_post!(id)
with {:ok, %Post{} = post} <- Api.update_post(post, post_params) do
render(conn, "show.json", post: post)
end
end
…
リクエストJSON
リクエストJSONのキーを無くしたいとき
そもそも、キーを無くしたい場合は、以前書いた、「mix phx.gen.jsonで作った既存APIの非DB化」の「リクエストJSONのバリデーションを解除する」をご参考ください
レスポンスJSON
レスポンスJSONのキーを変更するとき
Phoenixの自動生成JSON APIは、レスポンスJSONに「data」というキーを付与します
{
"data":
[
{
"id": 1,
"title":"t1",
"body": "b1"
}
]
}
これを以下のように変更します
{
"hoge":
[
{
"id": 1,
"title":"t1",
"body": "b1"
}
]
}
これを行うには、ビューのrender()2箇所の「%{data:~}」の「data」の部分を変更します
defmodule ApiSampleWeb.PostView do
def render("index.json", %{post: post}) do
%{hoge: render_many(post, PostView, "post.json")}
# ^---modify here
end
def render("show.json", %{post: post}) do
%{hoge: render_one(post, PostView, "post.json")}
# ^---modify here
end
…
レスポンスJSONのキーを無くしたいとき
Phoenixの自動生成JSON APIは、レスポンスJSONに「data」というキーが付与されますが、Vue.jsのaxiosなんかでこれを取得すると、response.data.data
みたいな書き方になり、ウザイので、キーを無くし、response.data
で取得できるようにしたい…なんてケースがあります
レスポンスJSONで言うと、以下のようにしたいときです
{
[
{
"id": 1,
"title":"t1",
"body": "b1"
}
]
}
これを行うには、ビューのrender()2箇所から、「data」で包むコードを削除します
defmodule ApiSampleWeb.PostView do
…
def render("index.json", %{post: post}) do
%{data: render_many(post, PostView, "post.json")}
end
def render("show.json", %{post: post}) do
%{data: render_one(post, PostView, "post.json")}
end
…
defmodule ApiSampleWeb.PostView do
…
def render("index.json", %{post: post}) do
render_many(post, PostView, "post.json")
# ^---%{data:} removed
end
def render("show.json", %{post: post}) do
render_one(post, PostView, "post.json")
# ^---%{data:} removed
end
…
コントローラからビューに渡すキーを変更したいとき
コードをキレイにしたいという観点で、リクエストJSONのキーだけで無く、コントローラからビューに渡すキーを変更したい場合は、render()呼び出しの第2引数のキーを変更し、受け側のビューのrender()も変更します
ついでに各種ローカル変数も変更しています(これは必須ではありませんが、変えておいた方が無難です)
defmodule ApiSampleWeb.PostController do
…
def index(conn, _params) do
hoge = Api.list_post()
# ^---modify here
render(conn, "index.json", hoge: hoge)
# modify here------^--------^-----^
end
def create(conn, %{"hoge" => hoge_params}) do
# modify here------^--------^
with {:ok, %Post{} = hoge} <- Api.create_post(hoge_params) do
# modify here-------^------------------------^
conn
|> put_status(:created)
|> put_resp_header("location", Routes.post_path(conn, :show, hoge))
# modify here-------------------------------------------------^
|> render("show.json", hoge: hoge)
# modify here-----------^-----^
end
end
def show(conn, %{"id" => id}) do
post = Api.get_post!(id)
render(conn, "show.json", hoge: hoge)
# modify here--------------^-----^
end
def update(conn, %{"id" => id, "hoge" => hoge_params}) do
# modify here------------------^--------^
hoge = Api.get_post!(id)
# ^---modify here
with {:ok, %Post{} = post} <- Api.update_post(hoge, hoge_params) do
# modify here-------^------------------------^-----^
render(conn, "show.json", hoge: hoge)
# modify here--------------^-----^
end
end
def delete(conn, %{"id" => id}) do
post = Api.get_post!(id)
# ^---modify here
with {:ok, %Post{}} <- Api.delete_post(post) do
# modify here-------------------------^
send_resp(conn, :no_content, "")
end
end
defmodule ApiSampleWeb.PostView do
…
def render("index.json", %{hoge: hoge}) do
# modify here-------------^-----^
%{data: render_many(hoge, PostView, "post.json")}
# modify here--------^
end
def render("show.json", %{hoge: hoge}) do
# modify here-------------^-----^
%{data: render_one(hoge, PostView, "post.json")}
# modify here-------^
end
…
なお、DBスキーマや、それに紐付く構造体(%Post{})、およびモジュール名まで変更する場合は、上記の続きを「気合い」で書き換えていくことも可能ですが、自動生成し直した方が速いと思われます
レスポンスをJSONでは無くテキストにしたい
render()の第一引数を index.json
から index.txt
に変更すれば、JSONからテキストに変更できます
ちなみに、あたかもファイル名のように見えますが、 index.json
や index.txt
というファイルが存在する訳ではありません
defmodule ApiSampleWeb.PostView do
…
def render("index.txt", %{hoge: hoge}) do
# modify here----^
"hoge, foo" # <--modify here
end
def render("show.txt", %{hoge: hoge}) do
# modify here---^
"hogehoge" # <--modify here
end
…
終わり
既存APIのリクエストJSON、レスポンスJSONをリネームする方法について解説しました
APIを自動生成する際にミスってしまったときや、自動生成したものに機能追加を行った後にリネームしたい、なんてときには、ご参考ください
p.s.「いいね」よろしくお願いします
ページ左上の や
のクリックを、どうぞよろしくお願いします
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください!