从0死磕全栈之Next.js Server Actions 入门实战:在服务端安全执行逻辑,告别 API 路由!

在 Next.js 13.4+ 中,官方正式推出 Server Actions(服务端函数)功能,让你无需编写 API 路由,就能直接在服务端处理表单提交、数据库操作等逻辑。本文通过一个简单的"留言提交"例子,带你快速掌握 Server Actions 的用法、优势和最佳实践。


一、什么是 Server Actions?

Server Actions 是 Next.js 提供的一种新机制,允许你在服务端组件中定义函数 ,并通过客户端(如表单)直接调用它,而无需创建单独的 /api 路由

核心特点:

  • ✅ 函数在服务端执行(安全!)
  • ✅ 自动处理 CSRF 保护
  • ✅ 支持 async/await
  • ✅ 与 React 表单天然集成
  • ✅ 自动序列化参数(支持对象、数组等)

💡 它不是替代 API 路由,而是为简单、私有、页面专属的逻辑提供更简洁的方案。


server action的概念其实来自于React的server function,指的是​​运行在服务器端的函数​​,通常用于处理业务逻辑、访问数据库、执行权限校验,Server Function 是一个通用术语,泛指任何运行在服务端的函数。

server action是 Server Function 的一种​​特定用法​​:​​专门用于处理表单提交、按钮点击等用户交互触发的异步操作​​.一个函数如果被标记为 ​​action​​(比如通过 action={myAction}传递),或者从一个 action函数内部调用,那么它就被称为 ​​Server Action​​。

二、为什么用 Server Actions? vs 传统 API 路由

场景 传统 API 路由 Server Actions
表单提交 需写 /api/submit + fetch 直接调用函数,无需 fetch
数据验证 在 API 文件中处理 在函数内直接处理
错误反馈 需解析 JSON 响应 直接抛出错误,自动捕获
安全性 需手动防 CSRF 自动内置保护
代码量 多文件(API + 页面) 单文件搞定

🎯 适用场景:用户注册、评论提交、点赞、设置偏好等页面级操作


三、实战例子:实现一个"留言提交"功能

我们将创建一个页面,用户输入名字和留言,点击提交后保存到内存(模拟数据库),并刷新列表。

⚠️ 注意:Server Actions 仅在 App Router 中可用


第一步:启用 Server Actions(Next.js 13.4+ 默认开启)

确保 next.config.js 中启用了(新版默认已开):

js 复制代码
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: true, // Next.js 14+ 可省略
  },
};
export default nextConfig;

第二步:创建留言页面

创建文件:app/guestbook/page.js

jsx 复制代码
// app/guestbook/page.js
'use client';

import { useState, useActionState } from 'react';
import { submitMessage } from './actions';

export default function Guestbook() {
  const [name, setName] = useState('');
  const [message, setMessage] = useState('');
  const [state, formAction] = useActionState(submitMessage, { success: false, error: null });

  const handleSubmit = (e) => {
    e.preventDefault();
    formAction({ name, message });
    // 清空表单(可选)
    setName('');
    setMessage('');
  };

  return (
    <div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
      <h1>📝 留言板</h1>

      {/* 显示结果 */}
      {state?.success && <p style={{ color: 'green' }}>✅ 留言成功!</p>}
      {state?.error && <p style={{ color: 'red' }}>{state.error}</p>}

      {/* 表单 */}
      <form onSubmit={handleSubmit}>
        <div>
          <input
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="你的名字"
            required
            style={{ display: 'block', margin: '0.5rem 0', padding: '0.5rem' }}
          />
        </div>
        <div>
          <textarea
            value={message}
            onChange={(e) => setMessage(e.target.value)}
            placeholder="留言内容"
            required
            rows="3"
            style={{ display: 'block', margin: '0.5rem 0', padding: '0.5rem' }}
          />
        </div>
        <button type="submit" style={{ padding: '0.5rem 1rem', backgroundColor: '#0070f3', color: 'white' }}>
          提交留言
        </button>
      </form>

      {/* 模拟显示留言(实际应从数据库读取) */}
      <div style={{ marginTop: '2rem' }}>
        <h2>已有留言</h2>
        <ul>
          <li>张三:Next.js 太棒了!</li>
          <li>李四:Server Actions 真香!</li>
        </ul>
      </div>
    </div>
  );
}

第三步:创建 Server Action(服务端函数)

创建文件:app/guestbook/actions.js

jsx 复制代码
// app/guestbook/actions.js
'use server';

// 模拟数据库(真实项目用 Prisma、Drizzle 等)
let messages = [];

export async function submitMessage(prevState, formData) {
  try {
    const { name, message } = formData;

    // 简单验证
    if (!name || !message) {
      return { success: false, error: '名字和留言不能为空!' };
    }

    // 模拟保存到数据库
    messages.push({ name, message, timestamp: new Date() });

    console.log('新留言:', { name, message });

    // 返回成功状态
    return { success: true, error: null };
  } catch (error) {
    return { success: false, error: '提交失败,请重试。' };
  }
}

🔑 关键点:

  • 文件顶部必须加 'use server'
  • 函数必须是 async
  • 第一个参数是前一次的状态(用于 useActionState),第二个是传入的数据。

第四步:运行并测试

bash 复制代码
npm run dev

访问 http://localhost:3000/guestbook,填写表单并提交。

你会看到:

  • 控制台打印日志(证明在服务端执行)
  • 页面显示"✅ 留言成功!"
  • 没有网络请求到 /api!数据直接通过 Server Action 处理

四、Server Actions 最佳实践

1. 只用于私有逻辑

  • 不要暴露敏感操作(如删除用户)给公开页面;
  • 适合当前用户专属操作(如设置偏好、提交评论)。

2. 配合身份验证

在函数内检查用户登录状态:

js 复制代码
'use server';
import { auth } from '@/auth';

export async function postComment(formData) {
  const session = await auth();
  if (!session) throw new Error('未登录');
  // ...保存评论
}

3. 错误处理要友好

  • 返回结构化状态(如 { success, error });
  • 避免直接 throw,除非你想中断流程。

4. 不要在客户端组件中定义

Server Action 必须定义在服务端上下文 中(即带 'use server' 的文件),不能写在 'use client' 组件里。

5. 参数自动序列化

支持传对象、数组、Date 等,但不能传函数、Symbol、undefined


五、常见问题 FAQ

Q1:Server Actions 能替代所有 API 路由吗?

不能。适合页面专属、简单操作;复杂、公共、需 CORS 的接口仍用 API 路由。

六、结语

Server Actions 是 Next.js 向"全栈一体化"迈出的重要一步。它让开发者用更少的代码、更安全的方式处理服务端逻辑,尤其适合表单交互场景。

记住:能用 Server Actions 解决的,就别写 API 路由了!

相关推荐
岁月宁静2 小时前
🎨 打造 AI 应用的 “门面”:Vue3.5 + MarkdownIt 实现高颜值、高性能的答案美化组件
前端·javascript·vue.js
光影少年2 小时前
vue3新增哪些内容以及api更改了哪些
前端·vue.js·掘金·日新计划
这儿有一堆花2 小时前
三种 弹出广告 代码开发实战
前端·html
练习时长一年3 小时前
Bean后处理器
java·服务器·前端
excel3 小时前
Vue 中 v-if 与 v-for 的优先级及最佳实践(Vue2 / Vue3 对比)
前端
吃饭最爱3 小时前
tomcat的功能和作用
前端
ObjectX前端实验室3 小时前
【图形编辑器架构】:编辑器的 Canvas 分层事件系统
前端·canvas·图形学
真的想不出名儿4 小时前
登录前验证码校验实现
java·前端·python
小高0074 小时前
前端如何优雅地生成唯一标识?——一份跨环境 UUID 工具函数的封装与实战
前端·javascript·vue.js