Phoenix 1.3.0 版本已發布
由 Chris McCord 於 2017 年 7 月 28 日張貼
Phoenix 1.3.0 版本已經釋出!此版本的重點在於具備改善專案結構的程式碼產生器、首要雨傘專案支援,以及將 Phoenix 強加為 Elixir 應用程式更大規模網際網路介面的鷹架。我們也在 Phoenix.Controller
中包含新的 action_fallback
功能,讓你可以將網域中常見的資料結構轉換為有效的回應。實際上,這可以減少控制器程式碼,並提供單一的地方來處理重複的程式碼路徑。這對 JSON API 控制器 來說尤其實用。此外,1.3 版本還包含頻道連線通訊協定的 V2 版本,它可以解決特定訊息模式下的競爭條件,並改善序列化格式。
若有興趣深入了解變更和設計決策,請看看我的 LonestarElixir 主旨演講:https://www.youtube.com/watch?v=tMO28ar0lW8。請注意,演講中的目錄結構稍微過期,但所有想法仍然適用。
若要使用新的 phx.new
專案產生器,你可以使用以下指令安裝檔案
$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez
1.3.0 版本對所有產生器使用 phx.
前置字元。舊的產生器仍存在,目的是給社群與學習資源時間迎頭趕上。它們將於 1.4.0 版本中移除。
和以往一樣,我們有 升級指南,其中有從 1.2.x 專案進行移轉的詳細說明。
1.3.0 版本向後相容,因此升級可能只需要將 mix.exs 中的 :phoenix
相依項提升到 “~> 1.3”。對於想要採用新規範的人來說,升級指南將會逐步說明。在升級之前,值得看看主旨演講或探討以下概述的設計決策。
Phoenix 1.3 – 有意圖的設計
新的專案和程式碼產生器會吸取過去兩年的經驗教訓,並在學習過程中引導人們做出更好的設計決策。新的專案有 lib/my_app
目錄,用於存放商業邏輯,以及 lib/my_app_web
目錄,用於存放所有與 Phoenix 相關的網頁模組,它們是 Elixir 應用程式更大規模的網路介面。隨著新的專案結構而來的是新的 phx.gen.html
和 phx.gen.json
產生器,它們採用將網頁介面與網域分開的目標。
內容
當您使用 phx.gen.html|json
產生 HTML 或 JSON 資源時,Phoenix 會產生位於 Context 中的程式碼。Context 是專用的模組,可公開與群組相關的功能。例如,每次呼叫 Elixir 的標準程式庫,無論是 Logger.info/1
還是 Stream.map/2
,您都在存取不同的 context。在內部,Elixir 的記錄器由多個模組組成,例如 Logger.Config
和 Logger.Backends
,但我們從未直接與這些模組互動。我們稱 Logger
模組為 context,正因為它公開並群組所有記錄功能。
例如,若要產生「使用者」資源,我們將執行
$ mix phx.gen.html Accounts User users email:string:unique
請注意「帳戶」是一個新的必要的第一個參數。這是您的程式碼將存在其中的 context 模組,它執行應用程式中使用者帳戶的業務邏輯。它可能包含驗證和使用者註冊等功能。以下是所產生程式碼的一部分
# lib/my_app_web/controllers/user_controller.ex
defmodule MyAppWeb.UserController do
...
alias MyApp.Accounts
def index(conn, _params) do
users = Accounts.list_users()
render(conn, "index.html", users: users)
end
def create(conn, %{"user" => user_params}) do
case Accounts.create_user(user_params) do
{:ok, user} ->
conn
|> put_flash(:info, "user created successfully.")
|> redirect(to: user_path(conn, :show, user))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
...
end
# lib/my_app/accounts/accounts.ex
defmodule MyApp.Accounts do
alias MyApp.Accounts.User
def list_users do
Repo.all(User)
end
def create_user(attrs \\ %{}) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
...
end
您還將在 lib/my_app/accounts/user.ex
內產生一個 Ecto 結構。請注意我們的控制器如何呼叫 API 界限以在系統中建立或擷取使用者。現在我們可以輕鬆地在其他控制器、在 Phoenix channels、在管理任務等中重複使用該邏輯。測試也變得更為直接,因為我們可以在不使用 Web 堆疊的情況下測試網域的內部和外部。
使用 context 進行設計,為您的應用程式發展奠定了堅實的基礎。使用離散的、定義完善的 API ,可公開您的系統意圖,讓您撰寫更具可維護性的應用程式,並包含可重複使用的程式碼。此外,我們只需瀏覽應用程式目錄結構,即可一窺應用程式的作用及其功能集。
lib
├── my_app
│ ├── accounts
│ │ ├── accounts.ex
│ │ └── user.ex
│ ├── sales
│ │ ├── manager.ex
│ │ ├── sales.ex
│ │ └── ticket.ex
│ └── repo.ex
├── my_app.ex
├── my_app_web
│ ├── channels
│ ├── controllers
│ ├── templates
│ └── views
└── my_app_web.ex
只要一瞥目錄結構,我們便可以看到此應用程式具有使用者帳戶系統,以及銷售系統。我們還可以推論,透過 sales.ex
和 accounts.ex
模組,這些系統之間存在一個自然 API。我們獲得此見解 而不用查看任何程式碼列。相對應以前的 web/models
,它並不顯示檔案之間的任何關聯,而大多反映了資料庫結構,並未提供有關它們如何實際與網域相關的見解
action_fallback
新的 action_fallback
功能允許您指定一個 plug,如果您的控制器動作無法傳回有效的 Plug.Conn{}
結構。然後 action fallback plug 的工作是在控制器動作之前接收連線,以及結果,並將其轉換為有效的 plug 回應。對於 JSON API 而言,這特別好,因為它消除了控制器之間的重複。例如,您的先前控制器可能看起來像這樣
def MyAppWeb.PageController do
alias MyApp.CMS
def show(conn, %{"id" => id}) do
case CMS.get_page(id, conn.assigns.current_user) do
{:ok, page} -> render(conn, "show.html", page: page)
{:error, :not_found} ->
conn
|> put_status(404)
|> render(MyAppWeb.ErrorView, :"404")
{:error, :unauthorized} ->
conn
|> put_status(401)
|> render(MyAppWeb.ErrorView, :"401")
end
end
end
這段程式碼本身沒問題,但問題在於我們的網域中常見的資料結構,例如 {:error, :not_found}
和 {:error, :unauthorized}
必須在許多不同的控制器中重複處理。現在有一個透過 action fallback 的更佳方式。有了 1.3 版本,我們可以編寫
def MyAppWeb.PageController do
alias MyApp.CMS
action_fallback MyAppWeb.FallbackController
def show(conn, %{"id" => id}) do
with {:ok, page} <- CMS.get_page(id, conn.assigns.current_user) do
render(conn, "show.html", page: page)
end
end
end
defmodule MyAppWeb.FallbackController do
def call(conn, {:error, :not_found}) do
conn
|> put_status(:not_found)
|> render(MyAppWeb.ErrorView, :"404")
end
def call(conn, {:error, :unauthorized}) do
conn
|> put_status(:unauthorized)
|> render(MyAppWeb.ErrorView, :"401")
end
end
請注意,我們的控制器現在可以使用 with
表示式符合正常的路徑。然後,我們可以指定一個 fallback 控制器在一個地方處理回應轉換。這對於程式碼清楚度和消除重複很有幫助。
我們對於這些變更及其在可維護性方面的長期回報感到興奮。我們也覺得它們將導致可共享的獨立程式庫,讓整個社群都能受益 - 在 Phoenix 相關專案中及之外。
如果你在升級方面有問題,請在 #elixir-lang irc 或 slack 上找到我們,我們會為你解決問題!
最後但也並非最不重要的一點,我想花一點時間感謝讓這個專案成為可能的公司。非常感謝 plataformatec 持續支援 Elixir 開發,以及感謝 DockYard 贊助 Phoenix。
編碼愉快! 🐥🔥
-Chris
完整變更紀錄
1.3.0-rc.3 (2017-07-24)
-
擴充功能
- [ChannelTest] 訂閱
connect
到UserSocket.id
來支援強制中斷測試 - [Socket] 定義頻道路由時支援靜態
:assigns
- [Channel] 加入纜線頻道纜線通訊協定的 V2,其中已解決競爭條件和壓縮資料負載
- [phx.new] 使用新的
lib/my_app
和lib/my_app_web
目錄結構 - [phx.new] 使用新的
MyAppWeb
別名慣例表示網頁模組 - [phx.gen.context] 不再用內容名稱為 Ecto 資料表加上前綴
- [ChannelTest] 訂閱
-
JavaScript 協力廠商擴充功能
- 使用 V2 頻道纜線通訊協定支援
-
JavaScript 協力廠商 bug 修正
- 在伺服器頻道順利加入時,解決客戶端加入逾時發生的競爭條件
1.3.0-rc.2 (2017-05-15)
請參閱 1.2.x
至 1.3.x
升級說明,讓現有的應用程式升級。
-
擴充功能
- [Generator] 新增新的
phx.new
、phx.new.web
、phx.new.ecto
專案產生器,它們有改進的應用程式結構和對遮蔽式應用程式的支援 - [Generator] 新增新的
phx.gen.html
和phx.gen.json
資源產生器,它們有改善的 API 邊界隔離 - [Controller] 新增
current_path
和current_url
來產生連線的路徑和網址 - [Controller] 簡介
action_fallback
來註冊一個 plug 作為控制器動作的 fallback 來呼叫 - [Controller] 在控制器中封裝例外情況以維護連線狀態
- [頻道] 新增設定頻道事件紀錄的功能,選項為
:log_join
與:log_handle_in
- [頻道] 警告未處理的
handle_info/2
訊息 - [頻道] 頻道現在可以區分正常退出與應用程式重新啟動,使用戶端能進入錯誤模式,並在冷部署後重新連線。
- [路由器] 編寫文件,說明
match
支援對應任何 HTTP 方法及特殊:*
參數 - [路由器] 使用路徑參數填入
conn.path_params
- [ConnTest] 新增
redirected_params/1
,傳回路由器對應的重定向網址中之命名參數 - [摘要處理器] 新增
mix phx.digest.clean
,以移除已編譯資源的舊版本 - [phx.new] 在
phx.new
安裝器檔案中新增 Erlang 20 支援
- [Generator] 新增新的
-
臭蟲修復
- [控制器] 強化限制當地重新導向,防止任意網址重新導向
- [控制器] 修復使用
clear_flash/1
時,快閃資料會保留的問題
-
已過時
- [產生器] 所有
phoenix.*
mix 任務已被phx.*
新任務取代,因此已過時
- [產生器] 所有
-
JavaScript 協力廠商擴充功能
- 新增傳遞
encode
與decode
函式至 Socket 建構函式的功能,用於自訂編碼與解碼傳出與傳入的訊息。 - 偵測使用者端的逾時心跳,處理非正常連線中斷,加快 Socket 錯誤偵測的速度
- 新增支援 AMD/RequireJS
- 新增傳遞