LoginSignup
15
8

More than 5 years have passed since last update.

Phoenix自動生成APIのリクエスト受付/レスポンス返却のコード解析

Last updated at Posted at 2018-06-18

(この記事は、「サーバーサイドプログラミング Advent Calendar 2017」の2日目です)


fukuoka.ex代表のpiacereです
ご覧いただいて、ありがとうございます :bow:

「季節外れのfukuoka.ex Advent Calendar」も、先週で終わりましたので、今回からは、小ネタ中心にスイッチします

今回は、「mix phx.gen.json ~」で自動生成したPhoenixのAPIが、クライアントから、どのような形でリクエストを受け付け、クライアントにレスポンスを返すのかのコードの流れを解析していきます

この解析を元に、DBテーブルと紐付くCRUDとして生成されたAPIを、DBテーブルとは関係無いAPIへと改造する方法を、次回以降のどこかで実装しようと思います


:shamrock::shamrock::shamrock: お礼:「いいね」638件いただき、毎週1/2/3フィニッシュ + 87回ランクイン達成 :shamrock::shamrock::shamrock:

4/27から6/13までの48日間に渡り、毎日お届けした、「季節外れのfukuoka.ex Elixir Advent Calendar」「季節外れのfukuoka.ex(その2) Elixir Advent Calender」ですが、沢山の応援をいただき、誠にありがとうございます :bow:

Elixirウィークリーランキングにて、開始の週から、終了の週までの8週間、毎週連続で1/2/3フィニッシュを飾りました :tada:

また、Qiitaトップページランキングには13回入賞し、各種ランキングにてトータル87回ものランクインを達成しました :ocean:

これらは一重に、皆さまからいただいた「いいね」によるもので、fukuoka.exアドバイザーズ/キャスト一同、感謝しています :wink:

image.png

Phoenix自動生成APIの作り方

Phoenixで自動生成するAPIの作り方は、「Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」のP22以降をご覧ください

image.png

なお、API自動生成以前に、Elixir/Phoenix/DBが、未だインストールされていない方は、「Excelから関数型言語マスター1回目:行の「並べ替え」と「絞り込み」」を第3回までなぞってください

以降では、上記手順にしたがって、以下コマンドで、Phoenix PJ作成/DBテーブル作成/API自動生成/Phoenix起動を行っている前提でお話します

mix phx.new sample_analytics --no-brunch --database=mysql
mix ecto.create
mix phx.gen.json Api Sample sample title:string body:string
【lib/sample_analytics_web/router.exにルート追加】
mix ecto.migrate
iex -S mix phx.server

APIのリクエストはコントローラで受け付ける

PhoenixのAPIは、下記コントローラモジュールの各関数で受け付けます

index()はGETで取得できるリスト一覧、create()はPOSTで行うデータ追加、show()はID指定のGETで取得できる各データ、update()はPUTで行うデータ更新、deleteはDELETEで行うデータ削除、といった感じに1対1で対応しています

lib/sample_analytics_web/controllers/sample_controller.ex
defmodule SampleAnalyticsWeb.SampleController do
  use SampleAnalyticsWeb, :controller

  alias SampleAnalytics.Api
  alias SampleAnalytics.Api.Sample

  action_fallback SampleAnalyticsWeb.FallbackController

  def index(conn, _params) do
    sample = Api.list_sample()
    render(conn, "index.json", sample: sample)
  end

  def create(conn, %{"sample" => sample_params}) do
    with {:ok, %Sample{} = sample} <- Api.create_sample(sample_params) do
      conn
      |> put_status(:created)
      |> put_resp_header("location", sample_path(conn, :show, sample))
      |> render("show.json", sample: sample)
    end
  end

  def show(conn, %{"id" => id}) do
    sample = Api.get_sample!(id)
    render(conn, "show.json", sample: sample)
  end

  def update(conn, %{"id" => id, "sample" => sample_params}) do
    sample = Api.get_sample!(id)

    with {:ok, %Sample{} = sample} <- Api.update_sample(sample, sample_params) do
      render(conn, "show.json", sample: sample)
    end
  end

  def delete(conn, %{"id" => id}) do
    sample = Api.get_sample!(id)
    with {:ok, %Sample{}} <- Api.delete_sample(sample) do
      send_resp(conn, :no_content, "")
    end
  end
end

index()は、DBデータ一覧をDBアクセッサであるApi.list_sample()経由で取得し、後述するビューモジュールのredner()でJSONオブジェクトリストとして返却します

show()は、引数で指定されたID指定でパターンマッチし、DBデータ1件をApi.get_sample!()経由で取得し、ビューモジュールのredner()にてJSONオブジェクトで返却します

create()は、リクエストで投げられた下記形式のJSONオブジェクトをパターンマッチし、Api.create_sample()にてバリデーション付insertを行い、insertが成功したら、show()同様に登録したデータ1件を、ビューモジュールのredner()にてJSONオブジェクトで返却します

DBデータ一覧をDBアクセッサであるApi.list_sample()経由で取得し、後述するビューモジュールのredner()でJSONオブジェクトリストとして返却します

なお、mixコマンドを使うと、下記のように、各HTTPメソッドと、コントローラ+関数の関係が表示できます

mix phx.routes
  page_path  GET     /            SampleAnalyticsWeb.PageController :index
sample_path  GET     /sample      SampleAnalyticsWeb.SampleController :index
sample_path  GET     /sample/:id  SampleAnalyticsWeb.SampleController :show
sample_path  POST    /sample      SampleAnalyticsWeb.SampleController :create
sample_path  PATCH   /sample/:id  SampleAnalyticsWeb.SampleController :update
             PUT     /sample/:id  SampleAnalyticsWeb.SampleController :update
sample_path  DELETE  /sample/:id  SampleAnalyticsWeb.SampleController :delete

APIのレスポンスJSON返却はビューで行う

PhoenixのAPIは、下記ビューモジュールのrender()で行います

コントローラのindex()は、render("index.json", ~)をパターンマッチして呼び出し、index()以外の全ては、render("show.json", ~)をパターンマッチして呼び出します

なお、"index.json"や"show.json"は、ファイル名では無く、コントローラからのパターンマッチ呼び分けのための文字列です

lib/sample_analytics_web/views/sample_view.ex
defmodule SampleAnalyticsWeb.SampleView do
  use SampleAnalyticsWeb, :view
  alias SampleAnalyticsWeb.SampleView

  def render("index.json", %{sample: sample}) do
    %{data: render_many(sample, SampleView, "sample.json")}
  end

  def render("show.json", %{sample: sample}) do
    %{data: render_one(sample, SampleView, "sample.json")}
  end

  def render("sample.json", %{sample: sample}) do
    %{id: sample.id,
      title: sample.title,
      body: sample.body}
  end
end

index()から呼ばれた際に使われるrender_many()は、DBデータ全件のマップリストが、JSONのオブジェクトリストとして返却されます

index()以外から呼ばれた際に使われるrender_one()は、DBデータ1件のマップを、JSONオブジェクトで返却します

そのいずれも、内部では、ビュー内のrender("sample.json", ~)を呼び出しており、render_many()/render_one()のいずれにも、引数で渡されたデータから、1データ分のマップを作成し、render_many()/render_one()に戻しています

render_many()は、DBデータ一覧をEnum.map()することで、複数回、render("sample.json", ~)を呼び出します

終わり

今回は、Phoenixで自動生成したAPIのコードの流れを解析しました

PhoenixのAPIにおける、リクエスト受付と、JSONレスポンス返却の流れが、理解できましたでしょうか?

この内容を理解すれば、DBテーブルと紐付くCRUDとして生成されたAPIを、DBテーブルとは関係無いAPIへと改造する方法も、何となく想像が付くのでは無いかと思います


:stars::stars::stars::stars::stars: お知らせ:Elixir MeetUpを6月末に開催します :stars::stars::stars::stars::stars:

「fukuoka.ex#11:DB/データサイエンスにコネクトするElixir」を6/22(金)19時に開催します

fukuoka.exの発足から、ちょうど1周年となる、記念的なイベントでもあるため、このコラムを気に入っていただいた方と、ぜひ一緒に盛り上がりたいです

福岡近辺にお住まいの方であれば、遊びに来てください

大人気により、一度は満席となりましたが、増枠しましたので、下記URLよりご参加ください
https://fukuokaex.connpass.com/event/87241

特別ゲストは、Erlang/Elixirの両面で世界的に有名な「力武 健次さん」と、北九州の飯塚市で「e-ZUKA Tech Night」を6年間主催し続けるハウインターナショナルの「谷口 耕平さん」のお二人と、実に豪華なイベントです

私は、今回のシリーズを踏まえた、「1家に1台、パーソナルなデータ分析AIを全員が持つ2020年を作る」というタイトルで、Elixirによる、ブラウザUI上からサクっとデータ分析プラットフォームを披露するLTをお届けします

image.png

p.s.「いいね」よろしくお願いします

よろしければ、ページ左上の image.pngimage.png のクリックをお願いしますー:bow:

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