LoginSignup
0

More than 5 years have passed since last update.

Phoenixを触ってみる その7

Last updated at Posted at 2018-12-09

だいぶ間空いてしまったけど。
今日はこちら https://hexdocs.pm/phoenix/controllers.html#content

Actions

  • Controller中の関数をactionと呼ぶ
  • routerと名前を合わせれば良い
  • actionは引数を2つ持つ
    • conn: リクエスト情報を持ってる。urlとか。仕組み自体はplugのもの
    • param: リクエストパラメータ

flash message

flashMessageっていう仕組みによって、通常のパラメータとは別で通知メッセージとかが渡せる

  • put_flash/3 でメッセージを渡す → viewとかでget_flashすることでそのメッセージを取得
  • 中見ると、connのprivateフィールドにこの機能用のkeyで値を入れている
  • バリデーションエラーとか更新成功しましたとかのメッセージで使うんかな

Rendering

今回のメイン、レスポンスを返す部分。
いくつかレスポンス返す用の関数がある。

  • text: plain textを表示するだけっていう
  • json: mapをjsonにして返す。apiで使いそう
  • html: htmlを返す
  • render: テンプレート使って返す

htmlあるけど、普通にrender使うよね。
renderに渡したい値は、renderの第3引数に渡すか、assignを使ってconnに突っ込む。
ちなみに、単にmapに入れてるだけなので後勝ち。

  def hello(conn, _params) do
    conn
    |> assign(:message, "hello world")
    |> render("index.html", message: "hoge)
  end

ちょっと横道それるけど。

こういうコード書くときに気にしたいのは、Javaとかと違ってイミュータブルなこと。
全部connを返り値としていて、それを引き継いだりactionの返り値としている。
なので、assignの返り値を無視してしまうと、値が設定されない。

  def helloNG(conn, _params) do
    # 参照渡しではないので、connの中身は変更されない
    assign(conn, :message, "this message is not set")
    ()
    render(conn, "index.html")
  end
  def helloOK(conn, _params) do
    # 書き換わったconnをその後の処理でも使う必要がある
    conn = assign(conn, :message, "this message is not set")
    ()
    render(conn, "index.html")
  end

で、おもしろいのが"plug"の仕組みを使えること。
前の回で書いたように、Module共通の処理がplugとして書ける。

plugの仕組み自体、connを引き回してくようにできているので、そこで値を渡すことで
Module内のデフォルト値のようなものを設定することが可能

when を使って、対象のactionを指定することも可能

Sending responses directly

  • send_resp/2を使うことで、レスポンスコードを指定してレスポンスを返せる
  • put_resp_content_type/2 使ってコンテンツタイプを指定できる

ということで、前述のhtmlとかtextってのも、実は中ではsend_respしているだけだったり。

Assigning Layouts

layoutがあるよって話だけど、詳細はたぶん後に出てくるので
Controllerからどう扱うかってところだけ。
(あと唐突に |> 時の注意点的なの出てくるけど、ここではスルーします

  • put_layout/2 で使うlayoutを指定できる。false渡すとlayout無しになる。
  • パスはlayoutのあるディレクトリから相対(つまりデフォルトは"app.html")

Overriding Rendering Formats

動的にtextかhtmlかを切り替えるよ、という話。
実際はjson or csv とかって使い方の方が多いと思うけど、、

  • _formatっていうパラメータを勝手に認識してくれる
  • テンプレートのディレクトリ内に、hoge.text.eexっていうのを作ると、_format=textの時にそちらが使われる
  • router.exのacceptsに"text"を追加すると通る

ちょっと実験

  • router.exのacceptsに"hoge"を追加
  • index.hoge.eexを作成
  • ?_format=hoge を付けてリクエスト

結果
なんかファイルがDLされた。中身はindex.hoge.eex。
render部分はhtml,text限らず文字列でマッチしてるだけ、
だけど、多分他のpipeとかで引っかからなくてContentsTypeとか決まらなかったんだろうな…

Setting the Content Type

で、コンテンツタイプの指定の仕方。特に言うことなし。

Setting the HTTP Status

  • 同様に、put_status/2 でステータスを指定できる
  • が、本当にステータスを変えるだけ。404とか指定しても404ページが表示されるわけではないし、302とかやってもリダイレクトしない
  • なのでエラーページにするとかリダイレクトする、っていうのは別の記述を。

基本ステータスと挙動はセットなので、あまり使うことはないかな?

Redirection

redirec/2を使うとリダイレクト出来るよって話。
第一引数がconnなのはいつもどおりとして、第二引数は使い分ける

  • to: 内部でのリダイレクトの場合。パスのみ渡す。
  • external: 外部へのリダイレクトの場合に使う。httpsからフルでURLを渡す・

Action Fallback

エラーハンドリングの話。
actionFallbackって形でplugを差し込むと、actionの後にそのplugが動かせるので、それを利用する。
このfallbackなplugもControllerという扱いらしい。

  • actionがエラーをreturnする
  • fallbackController内でエラー毎のcallを宣言してそれぞれのエラーに対する処理を書く
pageController.ex
  def show(conn, %{"id" => id}) do
    with {:ok, data} <- fetch(id) do
      render(conn, "index.html", message: "hello #{data}", name: data)
    end
  end
  defp fetch("hoge"), do: {:ok, "HogeHoge"}
  defp fetch(_id), do: {:error, :not_found}
fallbackController.ex
  def call(conn, {:error, :not_found}) do
    IO.inspect "on ontfound"
    conn
    |> put_status(:not_found)
    |> put_view(ErrorView)
    |> render(:"404")
  end

  def call(conn, {:error, :unauthorized}) do
    conn
    |> put_status(403)
    |> put_view(ErrorView)
    |> render(:"403")
  end

エラーハンドリングの話といってみたものの、実際のところエラーかどうかは関係なくて。
単にactionの後に行う処理を書けるよ、ってことらしい。

実際はこういう横断的に使う処理って、hwllo_web.exに書いたりするのかな。
流石に全Controllerに書いてくのはおかしい気がするので。

ちなみに。
PageController側でrenderした後に再度fallback側でrenderしても何も起きなかった
renderは早いもの勝ち?

PageController.ex
  def show(conn, %{"id" => id}) do
    with {:ok, data} <- fetch(id) do
      render(conn, "index.html", message: "hello #{data}", name: data)
    end
    {:error, :not_found}
    # errorを返しているが、↑のrenderを通っていれば画面上は200で返る。
  end

時間切れなので、いったんここまで。
続きはあとで追記する

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
0