伺服器動作 (Server Actions) 與資料異動 (Mutations)
伺服器動作 (Server Actions) 是在伺服器上執行的非同步函式。它們可以在伺服器與客戶端元件中呼叫,用於處理 Next.js 應用程式中的表單提交與資料異動。
🎥 觀看影片: 深入了解使用伺服器動作進行資料異動 → YouTube (10 分鐘)
慣例
伺服器動作可以使用 React 的 "use server"
指令來定義。您可以將指令放在 async
函式頂部以標記該函式為伺服器動作,或放在獨立檔案頂部以標記該檔案所有匯出為伺服器動作。
伺服器元件
伺服器元件可以使用函式層級或模組層級的 "use server"
指令。若要內嵌伺服器動作,請在函式主體頂部添加 "use server"
:
客戶端元件
要在客戶端元件中呼叫伺服器函式,請建立新檔案並在其頂部添加 "use server"
指令。檔案內所有匯出的函式將被標記為伺服器函式,可在客戶端與伺服器元件中重複使用:
將動作作為屬性傳遞
您也可以將伺服器動作作為屬性傳遞給客戶端元件:
通常,Next.js TypeScript 插件會標記 client-component.tsx
中的 updateItemAction
,因為它是一個通常無法跨客戶端-伺服器邊界序列化的函式。然而,名為 action
或以 Action
結尾的屬性被假定接收伺服器動作。這只是一個啟發式方法,因為 TypeScript 插件實際上不知道它接收的是伺服器動作還是普通函式。執行時類型檢查仍會確保您不會意外將函式傳遞給客戶端元件。
行為
- 伺服器動作可以使用
<form>
元素中的action
屬性來呼叫。- 伺服器元件預設支援漸進增強,意味著即使 JavaScript 尚未載入或已停用,表單仍會提交。
- 在客戶端元件中,呼叫伺服器動作的表單會在 JavaScript 尚未載入時排隊提交,優先考慮客戶端水合 (hydration)。
- 水合後,瀏覽器不會在表單提交時重新整理。
- 伺服器動作不限於
<form>
,可以從事件處理程序、useEffect
、第三方函式庫和其他表單元素(如<button>
)呼叫。 - 伺服器動作與 Next.js 快取和重新驗證架構整合。當動作被呼叫時,Next.js 可以在單次伺服器往返中返回更新的 UI 和新資料。
- 在幕後,動作使用
POST
方法,只有此 HTTP 方法可以呼叫它們。 - 伺服器動作的參數和返回值必須可由 React 序列化。請參閱 React 文件了解可序列化的參數和值。
- 伺服器動作是函式,這意味著它們可以在應用程式的任何地方重複使用。
- 伺服器動作繼承它們所使用頁面或佈局的執行環境。
- 伺服器動作繼承它們所使用頁面或佈局的路由區段配置,包括
maxDuration
等欄位。
範例
事件處理程序
雖然通常在 <form>
元素中使用伺服器動作,但它們也可以與 onClick
等事件處理程序一起呼叫。例如,增加按讚計數:
您也可以為表單元素添加事件處理程序,例如在 onChange
時儲存表單欄位:
對於這種情況,當可能快速連續觸發多個事件時,我們建議使用防抖 (debouncing) 來避免不必要的伺服器動作呼叫。
useEffect
您可以使用 React 的 useEffect
鉤子在元件掛載或依賴項變更時呼叫伺服器動作。這對於依賴全域事件或需要自動觸發的異動很有用。例如,onKeyDown
用於應用程式快捷鍵、無限滾動的交集觀察器鉤子,或在元件掛載時更新檢視計數:
請記住考慮 useEffect
的行為與注意事項。
錯誤處理
當錯誤被拋出時,它將被客戶端最近的 error.js
或 <Suspense>
邊界捕獲。請參閱錯誤處理以獲取更多資訊。
須知:
- 除了拋出錯誤外,您還可以返回一個物件供
useActionState
處理。
重新驗證資料
您可以在伺服器動作中使用 revalidatePath
API 重新驗證 Next.js 快取:
或者使用 revalidateTag
透過快取標籤使特定資料獲取失效:
重新導向
如果您希望在伺服器動作完成後將用戶重新導向到不同路由,可以使用 redirect
API。redirect
需要在 try/catch
區塊外呼叫:
Cookies
您可以在伺服器動作中使用 cookies
API 來 get
、set
和 delete
cookies:
請參閱其他範例了解如何從伺服器動作刪除 cookies。
安全性
預設情況下,當建立並匯出伺服器動作時,它會建立一個公開的 HTTP 端點,應以相同的安全假設和授權檢查來對待。這意味著,即使伺服器動作或工具函式未在程式碼的其他地方匯入,它仍然是公開可存取的。
為了提高安全性,Next.js 具有以下內建功能:
- 安全的動作 ID: Next.js 建立加密、非確定性的 ID,允許客戶端引用和呼叫伺服器動作。這些 ID 在構建之間定期重新計算以增強安全性。
- 死碼消除: 未使用的伺服器動作(由其 ID 引用)會從客戶端套件中移除,以避免第三方公開存取。
須知:
ID 在編譯期間建立,並快取最多 14 天。當啟動新構建或構建快取失效時,它們將重新生成。 此安全改進減少了缺少身份驗證層情況下的風險。然而,您仍應將伺服器動作視為公開 HTTP 端點。
身份驗證與授權
您應確保用戶有權執行該動作。例如:
閉包與加密
在元件內定義伺服器動作會建立一個閉包 (closure),其中動作可以存取外部函式的範圍。例如,publish
動作可以存取 publishVersion
變數:
閉包在您需要捕獲資料的快照(例如 publishVersion
)以便在動作被呼叫時使用時非常有用。
然而,為了實現這一點,捕獲的變數會被發送到客戶端,並在動作被呼叫時返回伺服器。為了防止敏感資料暴露給客戶端,Next.js 會自動加密閉包變數。每次構建 Next.js 應用程式時,都會為每個動作生成新的私鑰。這意味著動作只能在特定構建中被呼叫。
須知: 我們不建議僅依賴加密來防止敏感值暴露在客戶端。相反,您應使用 React taint API 主動防止特定資料發送到客戶端。
覆寫加密金鑰 (進階)
當您在多台伺服器上自行託管 Next.js 應用程式時,每個伺服器實例可能會產生不同的加密金鑰,導致潛在的不一致問題。
為解決此問題,您可以使用 process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY
環境變數覆寫加密金鑰。指定此變數可確保您的加密金鑰在多次建置間保持一致性,且所有伺服器實例都使用相同的金鑰。此變數必須使用 AES-GCM 加密。
這是進階使用情境,當您的應用程式需要確保在多個部署環境中加密行為一致時才需考慮。您應遵循標準安全實踐,例如金鑰輪替和簽章。
須知: 部署至 Vercel 的 Next.js 應用程式會自動處理此問題。
允許的來源 (進階)
由於伺服器動作 (Server Actions) 可以在 <form>
元素中觸發,這使其可能遭受 CSRF 攻擊。
在底層,伺服器動作使用 POST
方法,且僅允許此 HTTP 方法觸發它們。這在現代瀏覽器中可防止大多數 CSRF 漏洞,特別是當 SameSite cookies 成為預設值時。
作為額外保護,Next.js 中的伺服器動作還會比對 Origin 標頭 與 Host 標頭 (或 X-Forwarded-Host
)。若兩者不符,請求將被中止。換言之,伺服器動作只能在與宿主頁面相同的主機上觸發。
對於使用反向代理或多層後端架構的大型應用程式 (伺服器 API 與生產網域不同),建議使用 serverActions.allowedOrigins
配置選項來指定安全來源清單。此選項接受字串陣列。
了解更多關於 安全性與伺服器動作 的資訊。
延伸資源
更多資訊請參考以下 React 文件: