You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js API 处理器代码组织与错误处理最佳实践咨询

Next.js Pages Router API 处理器最优组织实践

核心优化思路

核心是拆分HTTP方法处理逻辑 + 通用能力高阶封装,避免所有逻辑耦合在单一根handler的switch分支中。

1. 封装通用高阶工具

首先封装可复用的错误处理、中间件高阶函数,解决重复代码问题:

// lib/api/withErrorHandler.ts
import { NextApiRequest, NextApiResponse } from 'next';

type ApiHandler = (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
type ErrorHandler = (err: unknown, req: NextApiRequest, res: NextApiResponse) => void;

const defaultErrorHandler: ErrorHandler = (err, req, res) => {
  console.error(err);
  res.status(500).json({ error: err instanceof Error ? err.message : '服务器内部错误' });
};

export const withErrorHandler = (handler: ApiHandler, customErrorHandler?: ErrorHandler): ApiHandler => {
  return async (req, res) => {
    try {
      await handler(req, res);
    } catch (err) {
      customErrorHandler ? customErrorHandler(err, req, res) : defaultErrorHandler(err, req, res);
    }
  };
};

鉴权中间件示例:

// lib/api/withProtected.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { verifyToken } from 'lib/auth';

export const withProtected = (handler: ApiHandler): ApiHandler => {
  return async (req, res) => {
    const token = req.headers.authorization?.split(' ')[1];
    if (!token) return res.status(401).json({ error: '未授权访问' });
    try {
      req.user = verifyToken(token);
      await handler(req, res);
    } catch {
      return res.status(401).json({ error: 'token无效' });
    }
  };
};

2. 拆分路由方法实现,按需组合高阶能力

改造后的pages/api/users/index.ts代码:

// pages/api/users/index.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { hash } from 'bcryptjs';
import prisma from 'lib/prisma';
import { withErrorHandler } from 'lib/api/withErrorHandler';
import { withProtected, withRole } from 'lib/api/middlewares';

// 单独实现GET方法:获取所有用户,需要管理员权限
const getUsersHandler = async (req: NextApiRequest, res: NextApiResponse) => {
  const users = await prisma.user.findMany();
  res.status(200).json({ users });
};
// 给GET方法套鉴权、角色校验、错误处理
const wrappedGetUsers = withErrorHandler(withRole('admin')(withProtected(getUsersHandler)));

// 单独实现POST方法:创建用户,无需鉴权,自定义错误处理
const createUserHandler = async (req: NextApiRequest, res: NextApiResponse) => {
  const { name, username, email, password: _password } = req.body;
  if (!name || !username || !email || !_password) {
    return res.status(400).json({ error: '缺少必填字段' });
  }
  const existUser = await prisma.user.findFirst({ where: { email } });
  if (existUser) throw new Error(`邮箱 ${email} 已被注册`);
  
  const password = await hash(_password, 10);
  const user = await prisma.user.create({
    data: { name, username, email, password }
  });
  res.status(201).json({ user });
};
// 自定义POST方法的错误处理
const postCustomErrorHandler = (err: unknown, req: NextApiRequest, res: NextApiResponse) => {
  if (err instanceof Error && err.message.includes('已被注册')) {
    return res.status(409).json({ error: err.message });
  }
  res.status(500).json({ error: '用户创建失败' });
};
const wrappedCreateUser = withErrorHandler(createUserHandler, postCustomErrorHandler);

// 根handler仅做方法分发
export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
): Promise<void> {
  switch (req.method) {
    case 'GET':
      return wrappedGetUsers(req, res);
    case 'POST':
      return wrappedCreateUser(req, res);
    default:
      res.setHeader('Allow', ['GET', 'POST']);
      res.status(405).end(`方法 ${req.method} 不被允许`);
  }
}

3. 复杂业务逻辑抽离到Service层

如果业务逻辑复杂度高,可将数据库操作、业务规则判断抽离到单独的service文件,例如services/user.service.ts,路由层仅负责参数接收、校验、调用service、返回响应,进一步降低路由文件的冗余度。

常见疑问解答

  • 错误处理方案合理性:将try catch封装为高阶函数是完全合理的工程化方案,既避免了每个分支重复编写try catch的冗余代码,也支持传入自定义错误处理逻辑满足不同分支的差异化需求,平衡了通用性和灵活性。
  • 中间件接入问题:拆分单个HTTP方法的处理函数后,每个方法可以独立按需组合任意中间件,不会对其他方法产生影响,完美解决了单handler处理多接口时中间件适配复杂的问题。

内容的提问来源于stack exchange,提问作者marko kraljevic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 23:06:00