PhoenixFrameworkとフロントエンドフレームワークを併用している場合、
mix phoenix.gen.json
でAPI作成してフロントからAjaxリクエストしてJSONを取得するという使い方をするのが主だと思います。
本記事はそんなAjaxリクエストに対して「ある条件の時にエラーを返したい・・・」となった時のTipsになります。
やり方
Ajaxリクエストでは特定のHTTPステータスコードが返ってきた場合にエラーになります。
どのHTTPステータスコードがエラーになるかは以下が参考になります。
jQueryのajaxが,HTTPステータスコードの何番がsuccessで,何番がerrorなのか調べました
ということはPhoenix側でエラーになるHTTPステータスコードを送ってあげればよいのです。
それではまずはmix phoenix.gen.json
で生成されたコントローラを見ていきましょう。
defmodule Test.UserController do
use Test.Web, :controller
alias Test.User
plug :scrub_params, "user" when action in [:create, :update]
def index(conn, _params) do
users = Repo.all(User)
render(conn, "index.json", users: users)
end
def create(conn, %{"user" => user_params}) do
changeset = User.changeset(%User{}, user_params)
case Repo.insert(changeset) do
{:ok, user} ->
conn
|> put_status(:created)
|> put_resp_header("location", user_path(conn, :show, user))
|> render("show.json", user: user)
{:error, changeset} ->
conn
|> put_status(:unprocessable_entity)
|> render(Test.ChangesetView, "error.json", changeset: changeset)
end
end
def show(conn, %{"id" => id}) do
user = Repo.get!(User, id)
render conn, "show.json", user: user
end
def update(conn, %{"id" => id, "user" => user_params}) do
user = Repo.get!(User, id)
changeset = User.changeset(user, user_params)
case Repo.update(changeset) do
{:ok, user} ->
render(conn, "show.json", user: user)
{:error, changeset} ->
conn
|> put_status(:unprocessable_entity)
|> render(Test.ChangesetView, "error.json", changeset: changeset)
end
end
def delete(conn, %{"id" => id}) do
user = Repo.get!(User, id)
# Here we use delete! (with a bang) because we expect
# it to always work (and if it does not, it will raise).
Repo.delete!(user)
send_resp(conn, :no_content, "")
end
end
index
create
show
update
delete
5つの関数が存在しますね。
今回はshow
に対してAjaxリクエストを行った場合に、問答無用でエラーになるようにしましょう。
def show(conn, %{"id" => id}) do
user = Repo.get!(User, id)
render conn, "show.json", user: user
end
このshow
の中身は2つの要素で出来ています。
- 引数の
id
に合致するユーザの取得 - ユーザ情報を
show.json
のテンプレートに当てはめて返す
それではこれをHTTPステータスコードの500を返すようにしてみましょう。
def show(conn, %{"id" => id}) do
conn
|> put_status(500)
# user = Repo.get!(User, id)
# render conn, "show.json", user: user
end
これで一旦500が返るようになりました。
ただこれだけでは不十分で大抵の場合、「何故エラーになったのか?」が必要なはずです。
次にJSON形式でエラーになった原因を送るようにします。
この時にキモとなるのがrender conn, "show.json", user: user
です。
この一文は「show.json
という形式にuser: user
を引数とした結果をレンダリングする」という意味になります。
ではこのshow.json
はどこで定義されているのでしょうか?
答えはviews
ディレクトリ内のviewファイルにありました。
defmodule Test.UserView do
use Test.Web, :view
def render("index.json", %{users: users}) do
%{data: render_many(users, Test.UserView, "user.json")}
end
# Showで使っているのはコレ!
def render("show.json", %{user: user}) do
%{data: render_one(user, Test.UserView, "user.json")}
end
def render("user.json", %{user: user}) do
%{id: user.id,
email: user.email,
name: user.name,
age: user.age,
info: user.info}
end
end
見て分かるようにshow.json
の定義がありますね。
今回はUser情報ではなくエラー情報を送りたいので、新しくerror.json
を定義しましょう。
defmodule Test.UserView do
use Test.Web, :view
def render("index.json", %{users: users}) do
%{data: render_many(users, Test.UserView, "user.json")}
end
# Showで使っているのはコレ!
#def render("show.json", %{user: user}) do
# %{data: render_one(user, Test.UserView, "user.json")}
#end
# 追加
def render("error.json", %{error: error}) do
%{error: error}
end
def render("user.json", %{user: user}) do
%{id: user.id,
email: user.email,
name: user.name,
age: user.age,
info: user.info}
end
end
これで引数に設定された%{error: error}
をそのまま返すような定義が出来ました。
次にコントローラーから呼び出してみましょう。
def show(conn, %{"id" => id}) do
conn
|> put_status(500)
|> render("error.json", error: "TEST ERROR")
# user = Repo.get!(User, id)
# render conn, "show.json", user: user
end
これでAjaxリクエストにエラーで返り、さらにerror
の中身を見ればエラー内容が記述されているはずです。
このように
-
put_status
でHTTPステータスコードを設定する - レンダリングするJSONを定義し、エラー内容を引数として渡す
といった方法でエラーにすることが出来ます。