檢視原始碼 Phoenix.Router (Phoenix v1.7.14)
定義 Phoenix 路由器。
路由器提供一組巨集,用於產生轉接到特定控制器和動作的路由。這些巨集以 HTTP 詞彙命名。例如
defmodule MyAppWeb.Router do
use Phoenix.Router
get "/pages/:page", PageController, :show
end
上述 get/3
巨集接受對 /pages/hello
的請求,並將其轉接到 PageController
的 show
動作,並在 params
中包含 %{"page" => "hello"}
。
Phoenix 的路由器極為有效率,因為它仰賴 Elixir 模式比對來比對路由和服務請求。
路由
以 HTTP 詞彙命名的 get/3
、post/3
、put/3
等巨集用於建立路由。
路由
get "/pages", PageController, :index
比對對 /pages
的 GET
請求,並將其轉接到 PageController
的 index
動作。
get "/pages/:page", PageController, :show
比對 /pages/hello
,並在 params
中包含 %{"page" => "hello"}
,轉接到 show
動作。
defmodule PageController do
def show(conn, params) do
# %{"page" => "hello"} == params
end
end
可以比對部分段落和多重段落。例如
get "/api/v:version/pages/:id", PageController, :show
比對 /api/v1/pages/2
,並在 params
中放入 %{"version" => "1", "id" => "2"}
。只能擷取段落的尾端部分。
從頭到尾比對路由。這裡的第二條路由
get "/pages/:page", PageController, :show
get "/pages/hello", PageController, :hello
永遠不會比對到 /pages/hello
,因為 /pages/:page
優先比對到它。
路由可以使用類別星號的型態來比對尾端段落。
get "/pages/*page", PageController, :show
比對 /pages/hello/world
,並將類別星號段落放入 params["page"]
中。
GET /pages/hello/world
%{"page" => ["hello", "world"]} = params
類別星號不能有字首或字尾,但可以和變數混合
get "/pages/he:page/*rest", PageController, :show
比對
GET /pages/hello
%{"page" => "llo", "rest" => []} = params
GET /pages/hey/there/world
%{"page" => "y", "rest" => ["there" "world"]} = params
為什麼使用巨集?
Phoenix 盡可能降低巨集的使用量。然而,您可能已經注意到
Phoenix.Router
大量依賴於巨集。為什麼會這樣?我們使用
get
、post
、put
和delete
來定義路由。我們使用巨集的目的是
它們定義路由引擎,在每個請求中用於選擇要將請求轉接到哪個控制器。多虧巨集,Phoenix 會編譯您的所有路由成一個單一 case-statement,其中包含模式比對規則,而這些規則已經由 Erlang VM 大幅最佳化
對於您定義的每個路由,我們也會定義元資料來實作
Phoenix.VerifiedRoutes
。正如我們很快就會了解到的,已驗證的路由允許我們將任何路由像參考一個正常的字串一樣,只不過它經過編譯器的驗證是有效的(使運送中斷的連結、表單、信件等事項到生產環境變得困難許多)換句話說,路由器仰賴巨集來建立更快速、更安全的應用程式。同時也要記住 Elixir 中的巨集只在編譯時期才會執行,這為編譯好的程式碼提供了充足的穩定性。Phoenix 透過
mix phx.routes
為所有已定義的路由提供內省。
產生路由
如要於您的應用程式內部產生路由,請參閱 Phoenix.VerifiedRoutes
文件,關於透過編譯時期驗證的基於 ~p
的路由產生方式,這是產生路由路徑和 URL 的優先方式。
Phoenix 也支援產生函式幫手,這是 Phoenix v1.6 及較早版本中的預設機制。我們將在接著進行的章節中介紹它。
幫手(已棄用)
預設情況下,Phoenix 在您的路由器內部產生一個 Helpers
模組,其中包含有助於開發人員產生與保持路由為最新的命名幫手。透過將 helpers: false
傳遞到 use Phoenix.Router
,即可停用幫手。
幫手是根據控制器的名稱自動產生的。例如,路由
get "/pages/:page", PageController, :show
將會產生以下的命名幫手
MyAppWeb.Router.Helpers.page_path(conn_or_endpoint, :show, "hello")
"/pages/hello"
MyAppWeb.Router.Helpers.page_path(conn_or_endpoint, :show, "hello", some: "query")
"/pages/hello?some=query"
MyAppWeb.Router.Helpers.page_url(conn_or_endpoint, :show, "hello")
"http://example.com/pages/hello"
MyAppWeb.Router.Helpers.page_url(conn_or_endpoint, :show, "hello", some: "query")
"http://example.com/pages/hello?some=query"
如果路由包含類似全域比對的樣式,那些參數就必須作為清單給予
MyAppWeb.Router.Helpers.page_path(conn_or_endpoint, :show, ["hello", "world"])
"/pages/hello/world"
在命名的 URL 幫手中產生的 URL 是根據 :url
、:http
和 :https
的設定。不過,如果您因為某些原因而需要手動控制 URL 的產生,URL 幫手也允許您傳入一個 URI
結構
uri = %URI{scheme: "https", host: "other.example.com"}
MyAppWeb.Router.Helpers.page_url(uri, :show, "hello")
"https://other.example.com/pages/hello"
也可以使用 :as
選項自訂命名的幫手。給定路由
get "/pages/:page", PageController, :show, as: :special_page
命名的幫手會是
MyAppWeb.Router.Helpers.special_page_path(conn, :show, "hello")
"/pages/hello"
範圍和資源
在 Phoenix 應用程式中,將所有路由都命名空間在應用程式範圍以下的情形非常普遍
scope "/", MyAppWeb do
get "/pages/:id", PageController, :show
end
上面的路由將會轉發給 MyAppWeb.PageController
。此語法不但對開發人員很方便(我們不必在所有路由上重複 MyAppWeb.
這個字首),而且也讓 Phoenix 能夠減少 Elixir 編譯器的負擔。如果我們寫成以下的樣子
get "/pages/:id", MyAppWeb.PageController, :show
Elixir 編譯器推論路由器直接取決於 MyAppWeb.PageController
,但這並非事實。Phoenix 可透過範圍提示 Elixir 編譯器,控制器並非路由器的實際依賴項。這能提供更有效率的編譯時間。
範圍讓我們能設定任何路徑或甚至輔助程式名稱的範圍
scope "/v1", MyAppWeb, host: "api." do
get "/pages/:id", PageController, :show
end
例如,上述路由將會比對路徑 "/api/v1/pages/1"
,且命名的路由將會是 api_v1_page_path
,就像 scope/2
選項所提供數值所預期的那樣。
就像所有路徑一樣,您能定義動態區段,這會套用至控制器的參數中
scope "/api/:version", MyAppWeb do
get "/pages/:id", PageController, :show
end
例如,上述路由將會比對路徑 "/api/v1/pages/1"
,且在控制器中的 params
引數將會有個具有 :version
鍵對應值 "v1"
的映射。
Phoenix 也提供了一個 resources/4
巨集,它可讓開發人員為給定的資源產生「RESTful」路由
defmodule MyAppWeb.Router do
use Phoenix.Router
resources "/pages", PageController, only: [:show]
resources "/users", UserController, except: [:delete]
end
最後,Phoenix 內含一個 mix phx.routes
工作,它可以漂亮地格式化給定路由器中的所有路由。我們能使用它來驗證上述路由器中所包含的所有路由
$ mix phx.routes
page_path GET /pages/:id PageController.show/2
user_path GET /users UserController.index/2
user_path GET /users/:id/edit UserController.edit/2
user_path GET /users/new UserController.new/2
user_path GET /users/:id UserController.show/2
user_path POST /users UserController.create/2
user_path PATCH /users/:id UserController.update/2
PUT /users/:id UserController.update/2
也可以將路由器明確地傳遞為工作中的引數
$ mix phx.routes MyAppWeb.Router
查看 scope/2
和 resources/4
以取得更多資訊。
管道與外掛程式
一旦要求到達 Phoenix 路由器,就會透過管道執行一系列轉換,直到要求被傳送到想要的路由。
此類轉換是透過外掛程式定義的,正如 Plug 規範書所定義的那樣。定義一個管道後,就可以將它透過每個範圍進行傳遞。
例如
defmodule MyAppWeb.Router do
use Phoenix.Router
pipeline :browser do
plug :fetch_session
plug :accepts, ["html"]
end
scope "/" do
pipe_through :browser
# browser related routes and resources
end
end
Phoenix.Router
從 Plug.Conn
和 Phoenix.Controller
匯入函式,以協助定義外掛程式。在上述範例中,fetch_session/2
來自 Plug.Conn
,而 accepts/2
來自 Phoenix.Controller
。
請注意,只有在找到路由時才會呼叫路由器管道。若未找到任何比對結果,則不會呼叫任何外掛程式。
如何組織我的路由?
在 Phoenix 中,我們傾向於定義提供特定功能的管道。舉例來說,以上的 pipeline :browser
包含所有路徑的常見 plug,這些路徑都是瀏覽器提取的。類似地,如果你也有服務 :api
要求,你會有一個獨立的 :api
管道,驗證你端點的特定資訊。
也許更重要的是,定義特定於驗證和授權的管道也很常見。舉例來說,你可能有要求所有使用者都經過驗證的管道。另一個管道可能僅強制管理者使用者能夠提取特定的路徑。由於路徑從上到下比對,建議在受限較少的路徑之前放置驗證/授權的路徑,以確保它們會優先比對。
當你的管道定義好後,你會在需要的範圍內重新使用管道,並將你的路徑分組到它們的管道周圍。舉例來說,想像你在建立一個部落格。任何人都能閱讀文章,但只有經過驗證的使用者才能建立文章。你的路徑會長這樣:
pipeline :browser do
plug :fetch_session
plug :accepts, ["html"]
end
pipeline :auth do
plug :ensure_authenticated
end
scope "/" do
pipe_through [:browser, :auth]
get "/posts/new", PostController, :new
post "/posts", PostController, :create
end
scope "/" do
pipe_through [:browser]
get "/posts", PostController, :index
get "/posts/:id", PostController, :show
end
注意以上路徑是如何在不同範圍內被分割的。儘管一開始這樣的分割會令人困惑,但它有一個很大的優點:你可以非常容易地檢查你的路徑,查看所有需要驗證的路徑,以及哪些路徑不需要。這有助於稽核並確保你的路徑有適當的範圍。
你可以建立你想要的範圍,可以建立少一點或多一點。因為管道可以在範圍之間重複使用,所以它們有助於封裝常見的功能,而且你可以在你定義的每個範圍中根據需要組合它們。
摘要
函式
產生一路徑的路由,來處理給定的路徑連線請求。
產生一路徑的路由,來處理給定的路徑刪除請求。
將給定路徑的請求轉送給一個 plug。
產生一路徑的路由,來處理給定的路徑取得請求。
產生一路徑的路由,來處理給定的路徑標頭請求。
根據任意 HTTP 方法產生一路徑的路由比對。
產生一個路由以處理對指定路徑的 options 請求。
產生一個路由以處理對指定路徑的 patch 請求。
定義一串我們將通過的 plug(和管線)。
定義一個 plug 管線。
在管線中定義一個 plug。
產生一個路由以處理對指定路徑的 post 請求。
產生一個路由以處理對指定路徑的 put 請求。
為一個資源定義「RESTful」路由。
傳回從指定路由器中的所有路由資訊。
定義一個路由可以嵌套其中的範圍。
使用指定的路徑定義一個範圍。
使用指定的路徑和別名定義一個範圍。
產生一個路由以處理對指定路徑的 trace 請求。
反向產生
針對一個要求,傳回編譯時間的路線資訊和執行時間的路徑參數。
這個 path
可以是字串,也可以是 path_info
的片段。
傳回一個含有以下金鑰的元資料對應:
:log
- 設定好的日誌等級。例如:debug
:path_params
- 執行時間路徑參數的對應:pipe_through
- 路由範圍的管線清單,例如[:browser]
:plug
- 將路由轉送至的 plug,例如AppWeb.PostController
:plug_opts
- 在呼叫 plug 時傳遞的選項,例如::index
:route
- 字串路由模式,例如"/posts/:id"
範例
iex> Phoenix.Router.route_info(AppWeb.Router, "GET", "/posts/123", "myhost")
%{
log: :debug,
path_params: %{"id" => "123"},
pipe_through: [:browser],
plug: AppWeb.PostController,
plug_opts: :show,
route: "/posts/:id",
}
iex> Phoenix.Router.route_info(MyRouter, "GET", "/not-exists", "myhost")
:error
傳回具有當前範圍別名字首的完整別名。
除了路由定義中的第二個參數之外,適用於將同一 shorthand 別名處理套用至其他值。
範例
scope "/", MyPrefix do
get "/", ProxyPlug, controller: scoped_alias(__MODULE__, MyController)
end
傳回具有當前範圍路徑字首的完整路徑。
函式
產生一路徑的路由,來處理給定的路徑連線請求。
connect("/events/:id", EventController, :action)
有關選項,請參閱 match/5
。
產生一路徑的路由,來處理給定的路徑刪除請求。
delete("/events/:id", EventController, :action)
有關選項,請參閱 match/5
。
將給定路徑的請求轉送給一個 plug。
與轉送字首相符的所有路徑都會傳送至轉送式外掛程式。這有助於讓多個應用程式共用某個路由器,甚至將某個大型路由器拆分為較小的路由器。在轉送連線之前會呼叫路由器管線。
然而,我們不建議轉送至其他端點。原因在於由您的應用程式與轉送端點所定義的外掛程式會被呼叫兩次,這可能會造成錯誤。
範例
scope "/", MyApp do
pipe_through [:browser, :admin]
forward "/admin", SomeLib.AdminDashboard
forward "/api", ApiRouter
end
產生一路徑的路由,來處理給定的路徑取得請求。
get("/events/:id", EventController, :action)
有關選項,請參閱 match/5
。
產生一路徑的路由,來處理給定的路徑標頭請求。
head("/events/:id", EventController, :action)
有關選項,請參閱 match/5
。
根據任意 HTTP 方法產生一路徑的路由比對。
對未包含在內建巨集中所定義的路由非常有幫助。
萬用法動詞 :*
也可使用於相符所有 HTTP 方法。
選項
:as
- 設定名為 helper。如果為nil
,則不會產生 helper。在使用已驗證路由時沒有作用:alias
- 設定是否套用範圍別名至路由。預設為 true,若為 false 則停用範圍。:log
- 記錄路由調用的層級,可以設定為 false。預設為:debug
。路由調用包含如何處理路由的資訊(呼叫哪個控制器動作、哪些參數可用以及使用哪些管線),並與外掛程式層級的紀錄分離。若要變更外掛程式紀錄層級,請參閱 https://hexdocs.dev.org.tw/phoenix/Phoenix.Logger.html#module-dynamic-log-level。:private
- 當路由相符時與連線合併的私密資料對應:assigns
- 當路由相符時與連線合併的資料對應:metadata
- 遠距測量事件所使用且由route_info/4
傳回的對應資料:warn_on_verify
- 布林值,用於判斷與此路由的相符項目是否觸發Phoenix.VerifiedRoutes
的不符路由警告。在驗證路由時,這有助於忽略否則為萬用法動詞的路由定義與相符項目。預設為false
。
範例
match(:move, "/events/:id", EventController, :move)
match(:*, "/any", SomeController, :any)
產生一個路由以處理對指定路徑的 options 請求。
options("/events/:id", EventController, :action)
有關選項,請參閱 match/5
。
產生一個路由以處理對指定路徑的 patch 請求。
patch("/events/:id", EventController, :action)
有關選項,請參閱 match/5
。
定義一串我們將通過的 plug(和管線)。
外掛使用接受一個 %Plug.Conn{}
和選項並傳回 %Plug.Conn{}
的進口 2 元函式的原子名稱來指定;例如,:require_authenticated_user
。
管道定義在路由器中;有關更多資訊,請參閱 pipeline/2
。
pipe_through [:my_imported_function, :my_pipeline]
定義一個 plug 管線。
管道在路由器根節點定義,可從任何範圍使用。
範例
pipeline :api do
plug :token_authentication
plug :dispatch
end
範圍可以將該管道像下面這樣使用
scope "/" do
pipe_through :api
end
每次呼叫 pipe_through/1
時,新管道會附加至先前給定的管道。
在管線中定義一個 plug。
有關更多資訊,請參閱 pipeline/2
。
產生一個路由以處理對指定路徑的 post 請求。
post("/events/:id", EventController, :action)
有關選項,請參閱 match/5
。
產生一個路由以處理對指定路徑的 put 請求。
put("/events/:id", EventController, :action)
有關選項,請參閱 match/5
。
參見 resources/4
.
參見 resources/4
.
為一個資源定義「RESTful」路由。
給定的定義
resources "/users", UserController
會包含下列動作的路由
GET /users
=>:index
GET /users/new
=>:new
POST /users
=>:create
GET /users/:id
=>:show
GET /users/:id/edit
=>:edit
PATCH /users/:id
=>:update
PUT /users/:id
=>:update
DELETE /users/:id
=>:delete
選項
此巨集接受一組選項
:only
- 例如,要為其產生路由的動作清單:[:show, :edit]
:except
- 例如,不概括在產生的路由的動作清單:[:delete]
:param
- 此資源的參數名稱,預設值為"id"
:name
- 此資源的前置詞。此用於命名的 helper,並作為巢狀資源中參數的前置詞。預設值自動從控制器名稱導出,例如:UserController
會有名稱"user"
:as
- 設定名為 helper。如果為nil
,則不會產生 helper。在使用已驗證路由時沒有作用:singleton
- 定義沒有參考身分的單一資源路由。請參閱以下了解更多資訊
單身資源
當需要在不參考身分的情況下檢索資源,因為它只在給定的情境中包含單一群組時,:singleton
選項可用於產生特定於該單一資源的一組路由
GET /user
=>:show
GET /user/new
=>:new
POST /user
=>:create
GET /user/edit
=>:edit
PATCH /user
=>:update
PUT /user
=>:update
DELETE /user
=>:delete
使用範例
resources "/account", AccountController, only: [:show], singleton: true
巢狀資源
此巨集也支援傳遞巢狀區塊的路由定義。對於將子資源巢狀於其父級以產生巢狀路由來說,這很有用。
給定的定義
resources "/users", UserController do
resources "/posts", PostController
end
將包含下列路由
user_post_path GET /users/:user_id/posts PostController :index
user_post_path GET /users/:user_id/posts/:id/edit PostController :edit
user_post_path GET /users/:user_id/posts/new PostController :new
user_post_path GET /users/:user_id/posts/:id PostController :show
user_post_path POST /users/:user_id/posts PostController :create
user_post_path PATCH /users/:user_id/posts/:id PostController :update
PUT /users/:user_id/posts/:id PostController :update
user_post_path DELETE /users/:user_id/posts/:id PostController :delete
傳回從指定路由器中的所有路由資訊。
定義一個路由可以嵌套其中的範圍。
範例
scope path: "/api/v1", alias: API.V1 do
get "/pages/:id", PageController, :show
end
上述產生的路由將符合路徑 "/api/v1/pages/:id"
,並會分派到 API.V1.PageController
中的 :show
動作。命名的 helper api_v1_page_path
也會產生。
選項
支援的選項如下
:path
- 包含路徑作用域的字串。:as
- 包含命名的 helper 作用域的字串或原子。設定為 false 時,會重設巢狀 helper 作用域。於獨佔使用驗證路由時不會產生影響:alias
- 包含控制器作用域的別名(原子)。設定為 false 時,會重設所有巢狀別名。:主機
- 包含主機範圍或主要主機範圍前綴的字串或字串清單,例如"foo.bar.com"
、"foo."
:private
- 當路由相符時與連線合併的私密資料對應:assigns
- 當路由相符時與連線合併的資料對應:log
- 記錄路由調用的層級,可以設定為 false。預設為:debug
。路由調用包含如何處理路由的資訊(呼叫哪個控制器動作、哪些參數可用以及使用哪些管線),並與外掛程式層級的紀錄分離。若要變更外掛程式紀錄層級,請參閱 https://hexdocs.dev.org.tw/phoenix/Phoenix.Logger.html#module-dynamic-log-level。
使用指定的路徑定義一個範圍。
這個函式是下列命令的捷徑
scope path: path do
...
end
範例
scope "/v1", host: "api." do
get "/pages/:id", PageController, :show
end
使用指定的路徑和別名定義一個範圍。
這個函式是下列命令的捷徑
scope path: path, alias: alias do
...
end
範例
scope "/v1", API.V1, host: "api." do
get "/pages/:id", PageController, :show
end
產生一個路由以處理對指定路徑的 trace 請求。
trace("/events/:id", EventController, :action)
有關選項,請參閱 match/5
。