どうも、靖宗です。
前回ざっくりとしかやらなかったルーティング。ちょっとページが長いので数回に分けて記事にしたいと思います。
router.ex
を見てみる
前回も触れていますが、ルーティングはlib/hello_phoenix_web/router.ex
に記載されています。
defmodule HelloPhoenixWeb.Router do use HelloPhoenixWeb, :router pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers end pipeline :api do plug :accepts, ["json"] end scope "/", HelloPhoenixWeb do pipe_through :browser get "/", PageController, :index end # Other scopes may use custom stacks. # scope "/api", HelloPhoenixWeb do # pipe_through :api # end end
use HelloPhoenixWeb, :router
の箇所でルーティングするためのマクロを使えるようにします。HelloPhoenixWeb
の__using__
は
defmacro __using__(which) when is_atom(which) do apply(__MODULE__, which, []) end
というマクロなので、HelloPhoenixWeb
のrouter/0
を実行しているようなものと思えば良さそうです。肝心のrouter関数は
def router do quote do use Phoenix.Router import Plug.Conn import Phoenix.Controller end end
となっており、PhoenixのRouterが参照され、scope
などのマクロが使える様になるといった流れです。
本来であればpipeline
やscope
などのマクロの実装もきちんと追った方が理解が深まるかと思いますが、内部の実装を詳しく知らなくても開発が進むようになっているのがマクロの筈です。今はスルーしてもっと内部実装を知りたいときに見ることにします。
現状の予想ではpipeline :browser
とpipeline :api
という書かれ方をしているので、おそらくブラウザからのアクセスとフロントエンドからのAPIとしてのアクセスとで細かい挙動を変えれるのだと思います。
pipeline
やpipe_through
は後々"Pipelines"という項目が出てくるようなのであとで詳細を確認します。
scope
中の
get "/", PageController, :index
に関しては前回拡張したとおり、GETメソッドでリクエストが来たときにコントローラーの関数(前回はレンダリング関数)を返すような動きをしていました。
第1引数("/")はパス(サーバーが置かれている場所の相対パス)で、第2,3引数はコントローラーとそのアクション(関数)です。モジュール名と関数が分離して与えられているのはMix and OTP編の分散処理あたりが内部実装としてあるのでしょう。
get
マクロはmatch/5
という関数の1節に該当しているようで、展開されると
def match(:get, "/", PageController, :index, [])
という実装になるそうです。
このget
マクロを列挙する形でページを増やしましたが、このget
句は他のウェブフレームワーク同様上から順に実行され、マッチすれば別のget
句は精査されないままレスポンスが完了するようです。
前回は"/"
から並べていきましたが、全部一番上のindexにならなかったのは完全一致だからなんでしょうか?
Examining Routes
Phoenixにはルーティングを確認する機能もついてるようです。プロジェクトのルートディレクトリ(一番上のディレクトリ)でmix phx.routes
というコマンドをうってやれば見れるみたいです。
PS > mix phx.routes
Compiling 2 files (.ex)
page_path GET / HelloPhoenixWeb.PageController :index
hello_path GET /hello HelloPhoenixWeb.HelloController :index
hello_path GET /hello/:messenger HelloPhoenixWeb.HelloController :show
page_path
やhello_path
は謎ですがあとで言及されるそうです。
GETメソッドでリクエストがどういうURLで来るとどのコントローラーのどのアクションが呼び出されるかが一目瞭然です。これは良さそう。
Resources
get
というマクロを使いましたが、これはGETメソッドと重なって直感的に挙動が分かりやすいです。
おそらくput
やpost
もあることでしょう。
ここで謎のマクロresources
が出てきます。とりあえず謎なので挙動を見てみます。
scope "/", HelloPhoenixWeb do pipe_through :browser get "/", PageController, :index resources "/users", UserController end
まだUserController
というコントローラーは実装していませんが、さっきおぼえたてのmix phx.routes
を使ってみます。
PS > mix phx.routes
Compiling 2 files (.ex)
page_path GET / HelloPhoenixWeb.PageController :index
user_path GET /users HelloPhoenixWeb.UserController :index
user_path GET /users/:id/edit HelloPhoenixWeb.UserController :edit
user_path GET /users/new HelloPhoenixWeb.UserController :new
user_path GET /users/:id HelloPhoenixWeb.UserController :show
user_path POST /users HelloPhoenixWeb.UserController :create
user_path PATCH /users/:id HelloPhoenixWeb.UserController :update
PUT /users/:id HelloPhoenixWeb.UserController :update
user_path DELETE /users/:id HelloPhoenixWeb.UserController :delete
なるほど!基本的にCRUD操作などをするデータの対象は、大体ルーティングが決まってると思います。
そのひな形をresources
というマクロを使うと一発で色々ルーティングできちゃうようです。
ちなみに:only
や:except
で実装を限定化できるようです。
resources "/posts", PostController, only: [:index, :show]
これはこうなります。
post_path GET /posts HelloPhoenixWeb.PostController :index post_path GET /posts/:id HelloPhoenixWeb.PostController :show
逆に:except
してみるとこうなります。(まぁ、:index
と:show
をexceptするなんてありえないですが)
resources "/posts", PostController, except: [:index, :show]
post_path GET /posts/:id/edit HelloPhoenixWeb.PostController :edit post_path GET /posts/new HelloPhoenixWeb.PostController :new post_path POST /posts HelloPhoenixWeb.PostController :create post_path PATCH /posts/:id HelloPhoenixWeb.PostController :update PUT /posts/:id HelloPhoenixWeb.PostController :update post_path DELETE /posts/:id HelloPhoenixWeb.PostController :delete
Forward
たぶんウェブリクエストを別のWebアプリケーションに投げる機能?
defmodule HelloWeb.Router do use HelloWeb, :router ... scope "/", HelloWeb do ... end forward "/jobs", BackgroundJob.Plug end
こんなかんじで書くらしい。
正直Plug
がなんなのかよく分かってないのでイマイチ文章が分かりませんでした。
とりあえず詰まったらまた戻るかんじで。
Path Helpers
以降は次回!