檢視原始碼 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 的請求,並將其轉接到 PageControllershow 動作,並在 params 中包含 %{"page" => "hello"}

Phoenix 的路由器極為有效率,因為它仰賴 Elixir 模式比對來比對路由和服務請求。

路由

以 HTTP 詞彙命名的 get/3post/3put/3 等巨集用於建立路由。

路由

get "/pages", PageController, :index

比對對 /pagesGET 請求,並將其轉接到 PageControllerindex 動作。

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 大量依賴於巨集。為什麼會這樣?

我們使用 getpostputdelete 來定義路由。我們使用巨集的目的是

  • 它們定義路由引擎,在每個請求中用於選擇要將請求轉接到哪個控制器。多虧巨集,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/2resources/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.RouterPlug.ConnPhoenix.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 請求。

反向產生

連結至這個函式

route_info(router, method, path, host)

檢視來源

針對一個要求,傳回編譯時間的路線資訊和執行時間的路徑參數。

這個 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
連結至這個函式

scoped_alias(router_module, alias)

檢視來源

傳回具有當前範圍別名字首的完整別名。

除了路由定義中的第二個參數之外,適用於將同一 shorthand 別名處理套用至其他值。

範例

scope "/", MyPrefix do
  get "/", ProxyPlug, controller: scoped_alias(__MODULE__, MyController)
end
連結至這個函式

scoped_path(router_module, path)

檢視來源

傳回具有當前範圍路徑字首的完整路徑。

函式

連結至這個巨集

connect(path, plug, plug_opts, options \\ [])

檢視來源 (巨集)

產生一路徑的路由,來處理給定的路徑連線請求。

connect("/events/:id", EventController, :action)

有關選項,請參閱 match/5

連結至這個巨集

delete(path, plug, plug_opts, options \\ [])

檢視來源 (巨集)

產生一路徑的路由,來處理給定的路徑刪除請求。

delete("/events/:id", EventController, :action)

有關選項,請參閱 match/5

連結至這個巨集

forward(path, plug, plug_opts \\ [], router_opts \\ [])

檢視來源 (巨集)

將給定路徑的請求轉送給一個 plug。

與轉送字首相符的所有路徑都會傳送至轉送式外掛程式。這有助於讓多個應用程式共用某個路由器,甚至將某個大型路由器拆分為較小的路由器。在轉送連線之前會呼叫路由器管線。

然而,我們不建議轉送至其他端點。原因在於由您的應用程式與轉送端點所定義的外掛程式會被呼叫兩次,這可能會造成錯誤。

範例

scope "/", MyApp do
  pipe_through [:browser, :admin]

  forward "/admin", SomeLib.AdminDashboard
  forward "/api", ApiRouter
end
連結至這個巨集

get(path, plug, plug_opts, options \\ [])

檢視來源 (巨集)

產生一路徑的路由,來處理給定的路徑取得請求。

get("/events/:id", EventController, :action)

有關選項,請參閱 match/5

連結至這個巨集

head(path, plug, plug_opts, options \\ [])

檢視來源 (巨集)

產生一路徑的路由,來處理給定的路徑標頭請求。

head("/events/:id", EventController, :action)

有關選項,請參閱 match/5

連結至這個巨集

match(verb, path, plug, plug_opts, options \\ [])

檢視來源 (巨集)

根據任意 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(path, plug, plug_opts, options \\ [])

檢視來源 (巨集)

產生一個路由以處理對指定路徑的 options 請求。

options("/events/:id", EventController, :action)

有關選項,請參閱 match/5

連結至這個巨集

patch(path, plug, plug_opts, options \\ [])

檢視來源 (巨集)

產生一個路由以處理對指定路徑的 patch 請求。

patch("/events/:id", EventController, :action)

有關選項,請參閱 match/5

連結至這個巨集

pipe_through(pipes)

檢視原始碼 (巨集)

定義一串我們將通過的 plug(和管線)。

外掛使用接受一個 %Plug.Conn{} 和選項並傳回 %Plug.Conn{} 的進口 2 元函式的原子名稱來指定;例如,:require_authenticated_user

管道定義在路由器中;有關更多資訊,請參閱 pipeline/2

pipe_through [:my_imported_function, :my_pipeline]
連結至這個巨集

pipeline(plug, list)

檢視原始碼 (巨集)

定義一個 plug 管線。

管道在路由器根節點定義,可從任何範圍使用。

範例

pipeline :api do
  plug :token_authentication
  plug :dispatch
end

範圍可以將該管道像下面這樣使用

scope "/" do
  pipe_through :api
end

每次呼叫 pipe_through/1 時,新管道會附加至先前給定的管道。

連結至這個巨集

plug(plug, opts \\ [])

檢視原始碼 (巨集)

在管線中定義一個 plug。

有關更多資訊,請參閱 pipeline/2

連結至這個巨集

post(path, plug, plug_opts, options \\ [])

檢視來源 (巨集)

產生一個路由以處理對指定路徑的 post 請求。

post("/events/:id", EventController, :action)

有關選項,請參閱 match/5

連結至這個巨集

put(path, plug, plug_opts, options \\ [])

檢視來源 (巨集)

產生一個路由以處理對指定路徑的 put 請求。

put("/events/:id", EventController, :action)

有關選項,請參閱 match/5

連結至這個巨集

resources(path, controller)

檢視原始碼 (巨集)

參見 resources/4.

連結至這個巨集

resources(path, controller, opts)

檢視原始碼 (巨集)

參見 resources/4.

連結至這個巨集

resources(path, controller, opts, list)

檢視原始碼 (巨集)

為一個資源定義「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(options, list)

View Source (巨集)

定義一個路由可以嵌套其中的範圍。

範例

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, options, list)

檢視原始碼 (巨集)

使用指定的路徑定義一個範圍。

這個函式是下列命令的捷徑

scope path: path do
  ...
end

範例

scope "/v1", host: "api." do
  get "/pages/:id", PageController, :show
end
連結至這個巨集

scope(path, alias, options, list)

檢視原始碼 (巨集)

使用指定的路徑和別名定義一個範圍。

這個函式是下列命令的捷徑

scope path: path, alias: alias do
  ...
end

範例

scope "/v1", API.V1, host: "api." do
  get "/pages/:id", PageController, :show
end
連結至這個巨集

trace(path, plug, plug_opts, options \\ [])

檢視來源 (巨集)

產生一個路由以處理對指定路徑的 trace 請求。

trace("/events/:id", EventController, :action)

有關選項,請參閱 match/5