
Next.js 如何返回 401 与 403 并自定义 unauthorized 和 forbidden 页面【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js在 App Router 应用里你经常需要区分两种请求未登录用户访问受保护页面时应返回401已登录但权限不足的用户访问时应返回403并且两种情况都要展示自己设计的提示页而不是框架默认内容。Next.js 提供了unauthorized与forbidden两个函数配合unauthorized.js/forbidden.js特殊文件来完成这件事。先说清楚适用前提这套能力在文档中标记为实验性功能。升级指南将forbidden、unauthorized、forbidden.js、unauthorized.js和authInterrupts列在 Features available in canary 之下且必须在next.config.js中显式开启authInterrupts才能使用next/navigation导入不会自动生效。开启 authInterrupts 开关这是使用这两个函数的必做步骤。在next.config.js中启用authInterruptsmodule.exports { experimental: { authInterrupts: true, }, }TypeScript 项目使用next.config.ts时import type { NextConfig } from next const nextConfig: NextConfig { experimental: { authInterrupts: true, }, } export default nextConfig在渲染路径中调用 unauthorized 与 forbiddenunauthorized用于未登录场景调用后会抛出NEXT_HTTP_ERROR_FALLBACK;401错误并终止当前路由片段的渲染Next.js 同时注入meta namerobots contentnoindex /防止该页被索引forbidden用于已登录但权限不足场景抛出NEXT_HTTP_ERROR_FALLBACK;403错误其余行为相同。两者都可以在 Server Components、Server FunctionsServer Actions和 Route Handlers 中调用但不能在 root layout 中调用。以 Server Component 为例未登录返回 401import { verifySession } from /app/lib/dal import { unauthorized } from next/navigation export default async function DashboardPage() { const session await verifySession() if (!session) { unauthorized() } return divDashboard/div }角色检查返回 403import { verifySession } from /app/lib/dal import { forbidden } from next/navigation export default async function AdminPage() { const session await verifySession() // Check if the user has the admin role if (session.role ! admin) { forbidden() } return ( main h1Admin Dashboard/h1 pWelcome, {session.user.name}!/p /main ) }示例中的verifySession来自/app/lib/dal和db来自/app/lib/db是文档示例中的会话校验与数据库辅助模块请替换为你自己的会话校验与数据访问代码函数签名按你的实现为准。这两个函数的工作方式是抛出异常TypeScript 返回类型为never所以不需要写return unauthorized()调用后执行即停止。在 Route Handler 中保护接口端点import { NextRequest, NextResponse } from next/server import { verifySession } from /app/lib/dal import { unauthorized } from next/navigation export async function GET(req: NextRequest): PromiseNextResponse { const session await verifySession() if (!session) { unauthorized() } // Fetch data // ... }在 Server Action 中保护敏感变更操作同理例如只允许 admin 更新角色use server import { verifySession } from /app/lib/dal import { forbidden } from next/navigation export async function updateRole(formData: FormData) { const session await verifySession() if (session.role ! admin) { forbidden() } // Perform the role update for authorized users // ... }自定义 unauthorized 与 forbidden 页面自定义 UI 通过unauthorized.js/forbidden.js特殊文件完成这两个文件不接收任何 props。文档示例同时提供.tsx与.js版本下文以.tsx为例。在app目录根部放置全局文件作为所有 401 场景的兜底 UIimport Login from /app/components/Login export default function Unauthorized() { return ( main h1401 - Unauthorized/h1 pPlease log in to access this page./p Login / /main ) }import Link from next/link export default function Forbidden() { return ( div h2Forbidden/h2 pYou are not authorized to access this resource./p Link href/Return Home/Link /div ) }其中Login /指向文档示例中的登录组件/app/components/Login替换为你项目自己的登录 UI 组件。也可以为单个路由就近放置文件来局部覆盖 UI。例如把权限检查放在Suspense边界内的数据加载函数中就在该路由旁添加unauthorized.tsximport Link from next/link export default function Unauthorized() { return ( main h1401 - Unauthorized/h1 p Please Link href/loginsign in/Link to view your account. /p /main ) }抛出异常后它会传播到最近的unauthorized/forbidden边界并渲染对应文件。验证结果按文档描述配置正确时可以得到以下可核对的结果状态码unauthorized.js文件约定下 Next.js 返回401状态码forbidden.js文件约定下返回403状态码页面内容请求未登录或权限不足时用户看到的是你自定义的unauthorized/forbidden页面而不是受保护的内容反爬取标记响应页面中会注入meta namerobots contentnoindex /失败信号如果异常没有被框架接住见下一节的未 await 场景开发环境的服务端会打印⨯ unhandledRejection: NEXT_HTTP_ERROR_FALLBACK;401forbidden对应403且页面不会渲染任何 unauthorized / forbidden UI——看到这个日志说明调用位置有问题。排查与限制检查位置必须在渲染路径上且必须被 await。由于这两个函数靠抛异常工作只有写在组件内部、或组件await的函数内部才会触发边界渲染。把unauthorized()放进一个未被 await 的 promise异常会抛到无人捕获的地方401 UI 不渲染开发环境只剩unhandledRejection日志。文档给出的模式是把校验放在数据访问层函数里并保证awaitimport { Suspense } from react import { verifySession } from /app/lib/dal import { unauthorized } from next/navigation async function getAccount() { const session await verifySession() if (!session) { unauthorized() } return db.accounts.findByUserId(session.userId) } async function AccountDetails() { const account await getAccount() return pSigned in as {account.email}/p } export default function AccountPage() { return ( main h1Account/h1 Suspense fallback{pLoading.../p} AccountDetails / /Suspense /main ) }try/catch 会吞掉中断。如果调用点被包在try/catch里异常会被你捕获401/403 UI 不再渲染。需要让框架异常穿透时在 catch 块开头调用unstable_rethrow(err)将其重新抛出参考 unstable_rethrow 文档。流式响应开始后状态码无法再变。如果检查发生在Suspense边界内响应可能已以200开始流式传输此时状态码不再能改成 401/403用户仍会看到自定义的 unauthorized / forbidden UI。要返回真实的 401/403 状态码检查必须发生在响应开始流式传输之前在使用 Cache Components 时动态路由会先流式输出静态 shell文档建议在proxy中执行该检查。root layout 不可调用unauthorized/forbidden。版本说明unauthorized、forbidden与对应的特殊文件均自v15.1.0引入且当前需配合实验性authInterrupts配置使用接入生产前建议按 升级指南确认你所用 Next.js 版本包含这些 canary 特性。更多细节可参考unauthorized 函数、forbidden 函数、unauthorized.js 文件约定、forbidden.js 文件约定、authInterrupts 配置。【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考