服务端操作与数据变更
服务端操作 (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 未加载时排队提交,优先进行客户端水合。
- 水合后,浏览器不会在表单提交时刷新。
- 服务端操作不仅限于
<form>
,可以从事件处理程序、useEffect
、第三方库和其他表单元素(如<button>
)调用。 - 服务端操作与 Next.js 的缓存和重新验证架构集成。当调用操作时,Next.js 可以在单个服务器往返中返回更新的 UI 和新数据。
- 在幕后,操作使用
POST
方法,只有这种 HTTP 方法可以调用它们。 - 服务端操作的参数和返回值必须可被 React 序列化。有关可序列化参数和值的列表,请参阅 React 文档。
- 服务端操作是函数,这意味着它们可以在应用程序的任何地方复用。
- 服务端操作继承它们所在页面或布局的运行时。
- 服务端操作继承它们所在页面或布局的路由段配置,包括
maxDuration
等字段。
示例
事件处理程序
虽然通常在 <form>
元素中使用服务端操作,但它们也可以通过 onClick
等事件处理程序调用。例如,增加点赞数:
你也可以为表单元素添加事件处理程序,例如在 onChange
时保存表单字段:
对于这种情况,当多个事件可能快速连续触发时,我们建议使用防抖以避免不必要的服务端操作调用。
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 端点。
身份验证和授权
你应确保用户有权执行操作。例如:
闭包和加密
在组件内定义服务端操作会创建一个闭包,其中操作可以访问外部函数的作用域。例如,publish
操作可以访问 publishVersion
变量:
闭包在需要捕获数据的快照(例如 publishVersion
)以便在操作调用时使用时非常有用。
然而,为了实现这一点,捕获的变量会被发送到客户端并在操作调用时返回到服务端。为了防止敏感数据暴露给客户端,Next.js 会自动加密闭包变量。每次构建 Next.js 应用时,都会为每个操作生成一个新的私钥。这意味着操作只能针对特定构建调用。
须知: 我们不建议仅依赖加密来防止敏感值暴露在客户端。相反,你应该使用 React taint APIs 主动防止特定数据发送到客户端。
覆盖加密密钥(高级用法)
在跨多台服务器自托管 Next.js 应用时,每个服务器实例可能会生成不同的加密密钥,从而导致潜在的不一致问题。
为解决此问题,您可以通过 process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY
环境变量覆盖加密密钥。指定该变量可确保加密密钥在构建过程中保持持久性,且所有服务器实例使用相同密钥。该变量 必须 使用 AES-GCM 加密。
这是需要跨多个部署保持加密行为一致性的高级使用场景。您应考虑采用密钥轮换和签名等标准安全实践。
须知: 部署到 Vercel 的 Next.js 应用会自动处理此问题。
允许的请求源(高级用法)
由于服务端操作 (Server Actions) 可以在 <form>
元素中调用,这会使其面临 CSRF 攻击 的风险。
在底层实现中,服务端操作使用 POST
方法,且仅允许通过该 HTTP 方法调用。这能防止现代浏览器中的大多数 CSRF 漏洞,特别是当 SameSite Cookie 成为默认设置时。
作为额外防护措施,Next.js 中的服务端操作还会比较 Origin 标头 与 Host 标头(或 X-Forwarded-Host
)。如果不匹配,请求将被中止。换句话说,服务端操作只能由托管它的页面所在的主机调用。
对于使用反向代理或多层后端架构(服务器 API 与生产域名不同)的大型应用,建议使用配置项 serverActions.allowedOrigins
来指定安全来源列表。该选项接受字符串数组。
了解更多关于 安全与服务端操作 的内容。
扩展资源
更多信息请查阅以下 React 文档: