LoginSignup
8
8

More than 5 years have passed since last update.

Phoenixで作成したAPIへのAjaxリクエストにエラーを返す方法

Posted at

PhoenixFrameworkとフロントエンドフレームワークを併用している場合、
mix phoenix.gen.jsonでAPI作成してフロントからAjaxリクエストしてJSONを取得するという使い方をするのが主だと思います。
本記事はそんなAjaxリクエストに対して「ある条件の時にエラーを返したい・・・」となった時のTipsになります。

やり方

Ajaxリクエストでは特定のHTTPステータスコードが返ってきた場合にエラーになります。
どのHTTPステータスコードがエラーになるかは以下が参考になります。
jQueryのajaxが,HTTPステータスコードの何番がsuccessで,何番がerrorなのか調べました

ということはPhoenix側でエラーになるHTTPステータスコードを送ってあげればよいのです。

それではまずはmix phoenix.gen.jsonで生成されたコントローラを見ていきましょう。

user_controller.ex
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リクエストを行った場合に、問答無用でエラーになるようにしましょう。

user_controller.ex
  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を返すようにしてみましょう。

user_controller.ex
  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ファイルにありました。

user_view.ex
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を定義しましょう。

user_view.ex
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}をそのまま返すような定義が出来ました。
次にコントローラーから呼び出してみましょう。

user_controller.ex
  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を定義し、エラー内容を引数として渡す

といった方法でエラーにすることが出来ます。

8
8
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
8
8