檢視原始碼 Phoenix.Endpoint 行為 (Phoenix v1.7.14)

定義 Phoenix 終端點。

終端點是 Web 應用程式中所有請求的開始點。它也是應用程式提供給後端 Web 伺服器的介面。

大致上,終端點有三個責任

  • 提供包覆器,作為監督樹的一部分,來啟動和停止終端點

  • 定義一個初始的 plug 管線,供請求通過

  • 為你的應用程式主機特定 Web 設定

終端點

終端點僅是一個模組,在 Phoenix.Endpoint 的協助下定義。如果你已使用 mix phx.new 產生器,終端點將會作為應用程式的一部分自動產生

defmodule YourAppWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :your_app

  # plug ...
  # plug ...

  plug YourApp.Router
end

終端點必須明確地作為應用程式監督樹的一部分啟動。在已產生的應用程式中,終端點會預設加入監督樹。終端點可以依下列方式加入監督樹

children = [
  YourAppWeb.Endpoint
]

終端點設定

所有終端點都在應用程式環境中設定。例如

config :your_app, YourAppWeb.Endpoint,
  secret_key_base: "kjoy3o1zeidquwy1398juxzldjlksahdk3"

終端點設定分為兩種類別。編譯時期設定意指設定會在編譯期間讀取,且在執行階段變更沒有作用。編譯時期設定主要與錯誤處理有關。

執行階段設定則是會在應用程式啟動期間或啟動後存取,且可以透過 config/2 函式讀取

YourAppWeb.Endpoint.config(:port)
YourAppWeb.Endpoint.config(:some_config, :default_value)

編譯時期設定

編譯時期設定可以在 config/dev.exsconfig/prod.exs 等等設定,但對 config/runtime.exs 沒有作用

  • :code_reloader - 當 true,啟用程式重新載入功能。有關程式重新載入組態選項清單,請參閱 Phoenix.CodeReloader.reload/1。請記住程式重新載入是基於檔案系統,因此不可能同時執行同一個應用程式的兩個執行個體,且同時在開發中重新載入程式,因為它們會彼此競賽,而只有一個會有效地重新編譯檔案。在這種情況下,請調整您的設定檔,使其僅在一個應用程式中啟用程式重新載入,或設定 MIX_BUILD 環境變數,以提供不同的建置目錄

  • :debug_errors - 當 true,使用 Plug.Debugger 功能來偵錯應用程式中的失敗。建議僅在開發期間將其設定為 true,因為它允許在偵錯期間列出應用程式原始碼。預設為 false

  • :force_ssl - 確保從來不會透過 HTTP 傳送任何資料,總是重新導向到 HTTPS。它預期有一份選項清單,這些選項會傳送到 Plug.SSL。預設會在 HTTPS 要求中設定「strict-transport-security」標頭,強制瀏覽器總是使用 HTTPS。如果傳送了一份不安全的請求 (HTTP),它會重新導向到 HTTPS 版本,使用在 :url 組態中指定的 :host。若要動態重新導向到目前的請求 host,請將 :force_ssl 組態中的 :host 設定為 nil

執行時期組態

以下組態可以設定在 config/dev.exsconfig/prod.exs 等等,以及 config/runtime.exs 中。通常,如果您需要使用系統環境變數組態它們,請在 config/runtime.exs 中設定它們。這些選項也可以在您的監督樹中啟動終端機時設定,例如 {MyApp.Endpoint, options}

  • :adapter - 使用哪個網頁伺服器介面來提供網頁請求。請參閱下方的「介面組態」區段

  • :cache_static_manifest - 一個 json 清單檔路徑,其中包含靜態檔案及其摘要版本。這通常設定為「priv/static/cache_manifest.json」,這個檔案是 mix phx.digest 自動產生的。它可以是:包含檔案系統路徑的字串,或包含應用程式名稱和該應用程式中的路徑的組元。

  • :cache_static_manifest_latest - 指向其摘要版本之靜態檔案的映射。這會在開機時從 cache_static_manifest 中自動載入。但是,如果您有自己的靜態處理機制,您可能希望明確設定此值。這是由如 LiveView 之類的專案用來偵測客戶端是在所有資產的最新版本上執行。

  • :cache_manifest_skip_vsn - 當為 true 時,在產生靜態資產的路徑時會略過附加的查詢字串 "?vsn=d"。此查詢字串由 Plug.Static 用來設定長到期日,因此,您應僅在不使用 Plug.Static 來提供資產的情況下將此選項設定為 true(例如,如果您使用的是 CDN)。如果您要設定此選項,您也應考慮將 --no-vsn 傳遞給 mix phx.digest。預設為 false

  • :check_origin - 設定傳輸的預設 :check_origin 設定。有關選項,請參閱 socket/3。預設為 true

  • :secret_key_base - 用作產生加密和簽署資料的秘密之基本秘密金鑰。例如,預設情況下會簽署 cookie 和權杖,但它們也可以依需要進行加密。預設為 nil,因為必須為每個應用程式設定秘鑰

  • :server - 當為 true 時,在端點監督樹狀結構啟動時啟動網路伺服器。預設為 falsemix phx.server 工作會自動將此設定為 true

  • :url - 針對整個應用程式產生 URL 的設定。接受 :host:scheme:path:port 選項。除了 :path 之外所有金鑰都可以在執行時期變更。預設為

    [host: "localhost", path: "/"]

    :port 選項需要整數或字串。 :host 選項需要字串。

    :scheme 選項接受 "http""https" 值。預設值從頂層 :http:https 選項推斷。這在將 Phoenix 託管在負載平衡器或反向代理並在那裡終止 SSL 時很有用。

    :path 選項可用於覆寫根路徑。當將 Phoenix 託管在具有 URL 重寫規則的反向代理後面時很有用

  • :static_url - 產生靜態檔案 URL 的設定。如果沒有提供任何選項,它會回退到 url。接受與 url 相同的選項

  • :watchers - 伺服器運作時的一組監控器。它需要包含可執行檔及其參數的元組清單。監控器保證會在應用程式目錄中執行,但僅限於伺服器已啟用的時候(除非 :force_watchers 設定為 true)。例如,當伺服器啟動時,下方的偵測器會執行 webpack 建置工具的「watch」模式。你可以將其設定為任何你想要的建置工具或指令

    [
      node: [
        "node_modules/webpack/bin/webpack.js",
        "--mode",
        "development",
        "--watch",
        "--watch-options-stdin"
      ]
    ]

    清單結尾可以用 :cd:env 選項自訂要監控的對象

    [node: [..., cd: "assets", env: [{"TAILWIND_MODE", "watch"}]]]

    偵測器也可以是將被適時呼叫的模組-函式-參數元組

    [another: {Mod, :fun, [arg1, arg2]}]
  • :force_watchers - 如果為 true,會強制你的偵測器在 :server 選項設定為 false 時啟動。

  • :live_reload - 即時重新載入選項的設定。設定需要一個 :patterns 選項,選項應為要監控的檔案模式清單。當這些檔案變更時,它會觸發重新載入。

    live_reload: [
      url: "ws://127.0.0.1:4000",
      patterns: [
        ~r"priv/static/(?!uploads/).*(js|css|png|jpeg|jpg|gif|svg)$",
        ~r"lib/app_web/(live|views)/.*(ex)$",
        ~r"lib/app_web/templates/.*(eex)$"
      ]
    ]
  • :pubsub_server - 在通道中和經由 Endpoin 廣播函式中要使用的 PubSub 伺服器。PubSub 伺服器通常在你的監督樹中啟動。

  • :render_errors - 每當應用程式出現錯誤時負責呈現範本。例如,如果應用程式在 HTML 請求期間發生 500 錯誤而崩潰,將會在提供給 :render_errors 的檢視中呼叫 render("500.html", assigns)。可以提供一個 :formats 清單,為每個格式指定一個模組以處理錯誤呈現。範例

    [formats: [html: MyApp.ErrorHTML], layout: false, log: :debug]
  • :log_access_url - 伺服器啟動後記錄存取 URL

請注意,你也可以將你自己的設定儲存在 Phoenix.Endpoint 中。例如,Phoenix LiveView 預期在 :live_view 鍵下有它自己的設定。在這種情況下,你應參閱相關專案的說明文件。

轉接設定

Phoenix 允許你選擇要使用哪個網路伺服器轉接器。透過 phx.new Mix 工作建立的新產生應用程式使用 Bandit 網路伺服器和 Bandit.PhoenixAdapter 轉接器。如果未透過 adapter 選項另行指定,Phoenix 會回歸到 Phoenix.Endpoint.Cowboy2Adapter 以維持與 Phoenix 1.7.8 之前產生的應用程式的向下相容性。

這兩個轉接器可以使用下列兩個頂層選項以類似的方式設定

  • :http - 對 HTTP 伺服器的設定。接受 BanditPlug.Cowboy 所定義的選項(依您選擇的轉接器而定)。預設為 false

  • :https - 對 HTTPS 伺服器的設定。接受 BanditPlug.Cowboy 所定義的選項(依您選擇的轉接器而定)。預設為 false

此外,也可以透過以下頂層選項來設定 Cowboy 網路伺服器的連線中止機制(這對 Bandit 來說並非必要,因為它的連線中止機制是內建的)

  • :drainer - 一個 drainer 程序會在應用程式關閉時等待所有正在進行的請求完成。它接受 Plug.Cowboy.Drainer 所定義的 :shutdown:check_interval 選項。注意,中止機制不會終止任何現有的連線,它只會等待它們完成。在呼叫這個中止機制之前,通訊端連線會執行它們自己的中止機制。這是因為通訊端會記錄狀態且可以進行順利通知,這讓我們得以在更長的一段時間內調整它們。詳情請參考 socket/3 的文件

終端 API

在上一節中,我們已經使用 config/2 函數,它會自動在您的終端中產生。以下是您的終端中會自動定義的所有函數清單

摘要

回呼函數

在指定topic中廣播一個msg訊息,格式為event,對象為所有節點。

在指定topic中廣播一個msg訊息,格式為event,對象為所有節點。

在指定topic中,從指定from廣播一個msg訊息,格式為event,對象為所有節點。

在指定topic中,從指定from廣播一個msg訊息,格式為event,對象為所有節點。

使用key存取終端機設定。

在應用程式更新時重新載入終端機設定。

從:url設定中傳回host。

在目前的節點中,使用指定topic,廣播一個msg訊息,格式為event

在目前的節點中,從指定from,廣播一個msg訊息,格式為event,對象為所有節點。

將路由導入此終端機時,產生相關資訊。

從:url設定中傳回script name。

傳回server執行的位址和埠。

啟動endpoint監督工作樹。

priv/static中產生靜態檔案的完整性雜湊。

產生包含static_pathstatic_integrity的二元組。

priv/static中產生靜態檔案的路由。

產生不含任何路徑資訊的靜態URL。

產生endpoint的基本URL,但需為URI結構。

讓呼叫者訂閱指定的topic。

讓呼叫者取消訂閱指定的topic。

產生不含任何路徑資訊的endpoint基本URL。

函數

檢查Endpoint的網路伺服器是否已設定啟動。

socket定義一個websocket/longpoll掛載點。

類型

@type event() :: String.t()
@type msg() :: map() | {:binary, binary()}
@type topic() :: String.t()

回呼

連結這個回呼

broadcast(topic, event, msg)

查看原始碼
@callback broadcast(topic(), event(), msg()) :: :ok | {:error, term()}

在指定topic中廣播一個msg訊息,格式為event,對象為所有節點。

連結這個回呼

broadcast!(topic, event, msg)

查看原始碼
@callback broadcast!(topic(), event(), msg()) :: :ok

在指定topic中廣播一個msg訊息,格式為event,對象為所有節點。

發生錯誤時引發例外。

連結這個回呼

broadcast_from(from, topic, event, msg)

查看原始碼
@callback broadcast_from(from :: pid(), topic(), event(), msg()) :: :ok | {:error, term()}

在指定topic中,從指定from廣播一個msg訊息,格式為event,對象為所有節點。

連結這個回呼

broadcast_from!(from, topic, event, msg)

查看原始碼
@callback broadcast_from!(from :: pid(), topic(), event(), msg()) :: :ok

在指定topic中,從指定from廣播一個msg訊息,格式為event,對象為所有節點。

發生錯誤時引發例外。

@callback config(key :: atom(), default :: term()) :: term()

使用key存取終端機設定。

連結這個回呼

config_change(changed, removed)

查看原始碼
@callback config_change(changed :: term(), removed :: term()) :: term()

在應用程式更新時重新載入終端機設定。

@callback host() :: String.t()

從:url設定中傳回host。

連結這個回呼

local_broadcast(topic, event, msg)

查看原始碼
@callback local_broadcast(topic(), event(), msg()) :: :ok

在目前的節點中,使用指定topic,廣播一個msg訊息,格式為event

連結這個回呼

local_broadcast_from(from, topic, event, msg)

查看原始碼
@callback local_broadcast_from(from :: pid(), topic(), event(), msg()) :: :ok

在目前的節點中,從指定from,廣播一個msg訊息,格式為event,對象為所有節點。

@callback path(path :: String.t()) :: String.t()

將路由導入此終端機時,產生相關資訊。

@callback script_name() :: [String.t()]

從:url設定中傳回script name。

@callback server_info(Plug.Conn.scheme()) ::
  {:ok,
   {:inet.ip_address(), :inet.port_number()} | :inet.returned_non_ip_address()}
  | {:error, term()}

傳回server執行的位址和埠。

@callback start_link(keyword()) :: Supervisor.on_start()

啟動endpoint監督工作樹。

啟動端點組態快取,以及可能用來處理請求的伺服器。

連結這個回呼

static_integrity(path)

查看原始碼
@callback static_integrity(path :: String.t()) :: String.t() | nil

priv/static中產生靜態檔案的完整性雜湊。

@callback static_lookup(path :: String.t()) ::
  {String.t(), String.t()} | {String.t(), nil}

產生包含static_pathstatic_integrity的二元組。

@callback static_path(path :: String.t()) :: String.t()

priv/static中產生靜態檔案的路由。

@callback static_url() :: String.t()

產生不含任何路徑資訊的靜態URL。

@callback struct_url() :: URI.t()

產生endpoint的基本URL,但需為URI結構。

連結這個回呼

subscribe(topic, opts)

查看原始碼
@callback subscribe(topic(), opts :: Keyword.t()) :: :ok | {:error, term()}

讓呼叫者訂閱指定的topic。

請參閱 Phoenix.PubSub.subscribe/3 以取得選項。

@callback unsubscribe(topic()) :: :ok | {:error, term()}

讓呼叫者取消訂閱指定的topic。

@callback url() :: String.t()

產生不含任何路徑資訊的endpoint基本URL。

函數

連結至該函數

server?(otp_app, endpoint)

查看原始碼

檢查Endpoint的網路伺服器是否已設定啟動。

  • otp_app - 執行端點的 OTP 應用程式,例如 :my_app
  • endpoint - 端點模組,例如 MyAppWeb.Endpoint

範例

iex> Phoenix.Endpoint.server?(:my_app, MyAppWeb.Endpoint)
true
連結至該巨集

socket(path, module, opts \\ [])

檢視原始碼 (巨集)

socket定義一個websocket/longpoll掛載點。

他會預期有一個 path、一個 socket 模組和一組選項。Socket 模組通常會使用 Phoenix.Socket 定義。

內建支援 websocket 和 longpolling 連線。

選項

  • :websocket - 控制 websocket 設定。預設為 true。可以設為 false 或關鍵字清單選項。有關完整清單,請參閱 「一般設定」「WebSocket 設定」

  • :longpoll - 控制 longpoll 設定。預設為 false。可以設為 true 或關鍵字清單選項。有關完整清單,請參閱 「一般設定」「Longpoll 設定」

  • :drainer - 關鍵字清單或自訂 MFA 函數,回傳關鍵字清單,例如

    {MyAppWeb.Socket, :drainer_configuration, []}

    設定如何在應用程式關閉時排空 socket。目標是通知所有頻道 (和 LiveView) 用戶端重新連線。支援的選項有

    • :batch_size - 每個批次要一次通知多少用戶端。預設值為 10000。
    • :batch_interval - 給定一個批次結束的時間 (毫秒)。預設值為 2000 毫秒。
    • :shutdown - 排空所有批次允許的最大時間 (毫秒)。預設值為 30000 毫秒。

    例如,如果你有 15 萬個連線,預設值會將它們分割成 15 個批次,每個批次有 1 萬個連線。在開始下一個批次之前,每個批次會間隔 2000 毫秒。在這個情況下,我們會在低於最大關閉時間 30000 毫秒內完成所有事情。因此,隨著你增加連線數,請記得調整相對應的關閉時間。最後,在 socket 排空器執行之後,較低層的 HTTP/HTTPS 連線排空器仍會執行,並套用至所有連線。設為 false 以停用排空。

你也可以在 use Phoenix.Socket 中傳入下方的選項。於此處指定的數值會覆寫 use Phoenix.Socket 中的數值。

範例

socket "/ws", MyApp.UserSocket

socket "/ws/admin", MyApp.AdminUserSocket,
  longpoll: true,
  websocket: [compress: true]

路徑參數

可以在路徑中包含變數,而這些變數可以在傳遞到 socket 的 params 中使用。

socket "/ws/:user_id", MyApp.UserSocket,
  websocket: [path: "/project/:project_id"]

一般設定

下方的設定可以同時使用於 :websocket:longpoll 鍵。

  • :path - 這是傳輸層的路徑。其預設值將是傳輸層名稱(「/websocket」或「/longpoll」)

  • :serializer - 訊息的序列化清單。更多資訊請參閱 Phoenix.Socket

  • :transport_log - 如果傳輸層本身要記錄,那就必須這麼做,並且設定紀錄等級。

  • :check_origin - 如果傳輸層在存在 origin 標題時,應檢查請求的來源。可能會是 truefalse、清單中允許的主機,或是以 MFA 元組提供的函式。預設值是端點設定中的 :check_origin 設定。

    如果為 true,標題會與 YourAppWeb.Endpoint.config(:url)[:host]:host 進行比對。

    如果為 false,且你並未在 socket 中驗證工作階段,你的應用程式會容易受到跨站式 WebSocket 劫持(CSWSH)攻擊。僅於開發階段使用,當主機真的不詳或於提供服務時,客戶端並未傳送 origin 標題,例如行動應用程式。

    你也可以指定清單中明確允許的來源。支援萬用字元。

    check_origin: [
      "https://example.com",
      "//another.com:888",
      "//*.other.com"
    ]

    或者,要接受任何與請求連線的主機、埠和架構相符的來源

    check_origin: :conn

    或者作為自訂 MFA 函式

    check_origin: {MyAppWeb.Auth, :my_check_origin?, []}

    MFA 會以請求 %URI{} 作為第一個引數進行呼叫,然後依序執行 MFA 清單中的引數,且必須回傳布林值。

  • :code_reloader - 啟用或停用程式碼重新載入器。預設值是你的端點設定。

  • :connect_info - 許多代表資料的鍵清單,會從傳輸層複製至使用者 socket connect/3 回呼中提供。有關有效鍵,請參閱「連接資訊」小節。

連接資訊

有效鍵為

  • :peer_data - Plug.Conn.get_peer_data/1 的結果

  • :trace_context_headers - 所有的追蹤內容標頭清單。支援的標頭由 W3C 追蹤內容規範 定義。這些標頭對於 OpenTelemetry 等函式庫,是必要的,以萃取追蹤傳遞資訊,讓你知道這個要求是一項正在進行的大型追蹤的一部分。

  • :x_headers - 所有以「x-」為開頭的要求標頭

  • :uri - 具備 conn 資訊的 %URI{}

  • :user_agent - 「user-agent」要求標頭的值

  • {:session, session_config} - Plug.Conn 的階段資訊。session_config 通常是傳送給 Plug.Session 的參數之完全副本。為了驗證階段,_csrf_token 必須在將 socket 與 URI.encode_www_form(Plug.CSRFProtection.get_csrf_token()) 的值連接時,作為要求參數提供。CSRF 令牌要求參數可透過 :csrf_token_key 選項修改。

    此外,session_config 可能為 MFA,例如 {MyAppWeb.Auth, :get_session_config, []},以允許在執行期間載入設定檔。

任意的關鍵字也可能出現在上述有效鍵的後方,這對於將自訂連線資訊傳遞給 socket 很實用。

例如

  socket "/socket", AppWeb.UserSocket,
      websocket: [
        connect_info: [:peer_data, :trace_context_headers, :x_headers, :uri, session: [store: :cookie]]
      ]

使用任意的關鍵字

  socket "/socket", AppWeb.UserSocket,
      websocket: [
        connect_info: [:uri, custom_value: "abcdef"]
      ]

我在哪裡能找到我的標頭 ?

出於安全考量,Phoenix 只會給你有限權限,可存取連線標頭。WebSocket 跨網域,表示當使用者「John Doe」造訪惡意的網站時,惡意的網站可以開啟與你的應用程式的 WebSocket 連線,而瀏覽器會很高興地送出 John Doe 的驗證/Cookie 資訊。假設你照單全收,惡意的網站就能完全控制與你的應用程式的 WebSocket 連線,並以 John Doe 的帳戶驗證。

為了保護你的應用程式,Phoenix 會限制並驗證 socket 可存取的連線資訊。這表示你的應用程式可免於這些攻擊,但你無法在 socket 中存取 Cookie 和其他標頭。你可以透過 :connect_info 選項,存取儲存在連線中的階段,前提是你也在透過 WebSocket 連線時傳遞了 csrf 令牌。

Websocket 設定檔

下列設定檔只適用於 :websocket

  • :timeout - 保持 websocket 連線開放的逾時時間,自它上次收到資料後起算,預設為 60,000 毫秒

  • :max_frame_size - 允許的最大幀大小 (以位元組為單位),預設為「無限大」

  • :fullsweep_after - socket 程序強制進行一輪完整清除前的最大垃圾收集次數。你可以將它設為 0 來強制更頻繁地清除你的 Websocket 傳輸程序。要設定此選項,需要 Erlang/OTP 24。

  • :compress - 是否針對所有資料框架啟用單一訊息壓縮,預設值為否。

  • :subprotocols - 一個支援的 Websocket 子協定的清單。用於握手的 Sec-WebSocket-Protocol 回應標頭,預設值為 nil。

    例如

    subprotocols: ["sip", "mqtt"]
  • :error_handler - 針對連線錯誤的自訂錯誤處理器。如果 Phoenix.Socket.connect/3 回傳一個 {:error, reason} 叢集,錯誤處理器將會以錯誤原因呼叫。對於 WebSockets,錯誤處理器必須是一個接收到 Plug.Conn、錯誤原因的 MFA 叢集,並回傳一個帶有回應的 Plug.Conn。例如:

    socket "/socket", MySocket,
        websocket: [
          error_handler: {MySocket, :handle_error, []}
        ]

    MySocket 上,回傳 {:error, :rate_limit} 可能是由

    def handle_error(conn, :rate_limit), do: Plug.Conn.send_resp(conn, 429, "Too many requests")

Longpoll組態

以下組態只適用於 :longpoll

  • :window_ms - 使用者可以在毫秒(ms)為單位在其輪循要求中等待新訊息的長度。預設值為 10_000

  • :pubsub_timeout_ms - 一個要求可以等待 pubsub 層回應的長度,以毫秒(ms)為單位。預設值為 2000

  • :crypto - 由 Phoenix.Token 接受,用於驗證和簽署令牌的選項。預設情況下,令牌有效期為 2 週。