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

Next.js 13 App Router中如何在多个路由处理器间共享逻辑?

Next.js 13路由处理器通用登录验证的集中处理方案

Next.js 13 App Router的路由处理器(Route Handler)目前没有官方提供的类似Layout的共享逻辑机制,官方文档也未提及相关功能。针对你提到的通用登录验证需求,这里有几个简洁实用的替代方案:

方案1:封装通用验证函数,在路由处理器中显式调用

这是最直接的方式,把登录验证、Cookie读取、错误返回的逻辑封装成独立函数,在需要的API路由里直接调用。

示例代码:
首先创建lib/auth.ts:

import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';

export async function validateUserSession() {
  const cookieStore = cookies();
  const authToken = cookieStore.get('auth_token')?.value;

  // 模拟验证逻辑:检查token是否存在、是否过期
  if (!authToken) {
    return NextResponse.json({ error: '未登录' }, { status: 401 });
  }
  if (isTokenExpired(authToken)) { // 假设你有自己的过期检查函数
    return NextResponse.json({ error: '登录已过期' }, { status: 401 });
  }

  // 验证通过,返回用户信息(如果需要)
  const user = await fetchUserInfoByToken(authToken); // 假设你有获取用户信息的逻辑
  return { user };
}

// 辅助函数示例
function isTokenExpired(token: string) {
  // 实际的过期检查逻辑
  return false;
}

async function fetchUserInfoByToken(token: string) {
  // 实际的用户信息获取逻辑
  return { id: 1, name: 'test' };
}

然后在需要验证的API路由(比如app/api/profile/route.ts)中使用:

import { validateUserSession } from '@/lib/auth';
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const validationResult = await validateUserSession();
  
  // 如果返回的是NextResponse,说明验证失败,直接返回错误响应
  if (validationResult instanceof NextResponse) {
    return validationResult;
  }

  // 验证通过,执行API逻辑
  return NextResponse.json({ user: validationResult.user });
}

优点:逻辑清晰,无需额外配置,适合少量需要验证的API;缺点:每个需要验证的路由都要手动调用函数,存在少量重复代码。

方案2:使用高阶函数(HOC)包裹路由处理器

把验证逻辑封装成高阶函数,用它来包裹需要验证的路由处理器,实现逻辑的自动注入,减少重复代码。

示例代码:
在lib/auth.ts中添加高阶函数:

import { cookies } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';

// 定义路由处理器的类型
type RouteHandler = (req: NextRequest) => Promise<NextResponse>;

export function withAuth(handler: RouteHandler): RouteHandler {
  return async (req: NextRequest) => {
    const cookieStore = cookies();
    const authToken = cookieStore.get('auth_token')?.value;

    // 验证逻辑和之前一致
    if (!authToken) {
      return NextResponse.json({ error: '未登录' }, { status: 401 });
    }
    if (isTokenExpired(authToken)) {
      return NextResponse.json({ error: '登录已过期' }, { status: 401 });
    }

    // 验证通过,执行原处理器
    return handler(req);
  };
}

// 复用之前的辅助函数
function isTokenExpired(token: string) {
  return false;
}

然后在API路由中使用:

import { withAuth } from '@/lib/auth';
import { NextResponse } from 'next/server';

// 用withAuth包裹原处理器
export const GET = withAuth(async (request) => {
  // 这里直接写验证通过后的API逻辑
  return NextResponse.json({ message: '获取用户信息成功' });
});

优点:路由处理器代码更简洁,验证逻辑统一维护;缺点:如果需要传递用户信息到原处理器,需要额外处理(比如通过request对象挂载,或者修改高阶函数返回值)。

方案3:路由分组+Middleware(优化维护性)

你提到Middleware维护性不佳,主要是因为要检查所有API URL。可以通过路由分组把需要验证的API归类到同一组,然后Middleware只针对该分组路径做验证,避免全局匹配。

步骤:

  1. 创建路由分组,比如把需要验证的API放在app/api/auth-protected/(routes)/目录下,比如app/api/auth-protected/(routes)/profile/route.ts、app/api/auth-protected/(routes)/orders/route.ts。
  2. 在middleware.ts中只匹配该分组的路径:
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 只针对auth-protected分组下的API做验证
  if (request.nextUrl.pathname.startsWith('/api/auth-protected/')) {
    const authToken = request.cookies.get('auth_token')?.value;

    if (!authToken || isTokenExpired(authToken)) {
      return NextResponse.json({ error: '未登录或登录过期' }, { status: 401 });
    }
  }

  // 其他路径直接放行
  return NextResponse.next();
}

// 配置Middleware的匹配规则,只匹配auth-protected分组
export const config = {
  matcher: '/api/auth-protected/:path*',
};

// 辅助函数示例
function isTokenExpired(token: string) {
  return false;
}

优点:批量保护一组API,无需在每个路由中写验证逻辑;缺点:需要调整路由结构,且Middleware中无法直接使用App Router的cookies()函数(只能使用request.cookies),如果需要复杂的服务器端逻辑,可能不如前两个方案灵活。

内容的提问来源于stack exchange,提问作者Lee KyuSung

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 08:50:33