从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 分钟前
一体化系统(九)智慧社区综合报表——东方仙盟练气期
大数据·前端·仙盟创梦ide·东方仙盟·东方仙盟一体化
陈天伟教授3 小时前
人工智能训练师认证教程(2)Python os入门教程
前端·数据库·python
信看4 小时前
NMEA-GNSS-RTK 定位html小工具
前端·javascript·html
Tony Bai4 小时前
【API 设计之道】04 字段掩码模式:让前端决定后端返回什么
前端
苏打水com4 小时前
第十四篇:Day40-42 前端架构设计入门——从“功能实现”到“架构思维”(对标职场“大型项目架构”需求)
前端·架构
king王一帅4 小时前
流式渲染 Incremark、ant-design-x markdown、streammarkdown-vue 全流程方案对比
前端·javascript·人工智能
苏打水com5 小时前
第十八篇:Day52-54 前端跨端开发进阶——从“多端适配”到“跨端统一”(对标职场“全栈化”需求)
前端
Bigger5 小时前
后端拒写接口?前端硬核自救:纯前端实现静态资源下载全链路解析
前端·浏览器·vite
BD_Marathon5 小时前
【JavaWeb】路径问题_前端绝对路径问题
前端