← [ BACK_TO_NOTES ]

Next.js Not Found 处理

Next.js App Router 用 notFound() 与 not-found.tsx 表达资源不存在这一预期业务状态,将 404 与真正的系统异常清晰分离。

#concept #growing #frame #react

[!info] related notes

Next.js Not Found 处理

一句话定义

notFound() 与 not-found.tsx 用于表达:请求链路本身正常,但目标资源不存在,因此应该进入 404 语义,而不是把它当成未处理异常。

最小例子

import { notFound } from "next/navigation"

export default async function Page({
  params,
}: {
  params: Promise<{ goalId: string }>
}) {
  const { goalId } = await params
  const goal = await getGoal(goalId)

  if (!goal) {
    notFound()
  }

  return <GoalDetail goal={goal} />
}

配合:

goals/
└── [goalId]/
    ├── page.tsx
    └── not-found.tsx

就可以给不存在的 Goal 提供专门 404 UI。

Expected Absence 与 Exceptional Failure

这是这篇笔记最重要的语义区分。

资源不存在

数据库查询成功
      ↓
结果 = null
      ↓
notFound()
      ↓
not-found.tsx

这属于:

Expected absence / resource not found

系统异常

数据库连接失败
      ↓
未处理异常
      ↓
error.tsx

这属于:

Unexpected failure

因此:

404 ≠ 500
Not Found ≠ Error

为什么不要直接 throw new Error("not found")

错误写法:

if (!goal) {
  throw new Error("Goal not found")
}

这会把一个正常、可预期的资源状态伪装成系统故障。

后果可能包括:

  • 错误监控产生噪声
  • 用户看到错误恢复 UI,而不是明确的 404
  • 代码失去 HTTP / 领域语义
  • 运维把正常访问不存在资源误判成服务异常

所以:

Expected absence should remain expected.

notFound() 是控制流

notFound() 不只是返回一个布尔值。调用它以后,当前 route segment 的正常渲染流程会终止,并切换到对应的 Not Found UI。

因此代码可以直接写:

if (!goal) {
  notFound()
}

return <GoalDetail goal={goal} />

不需要再手工写 return null。

与动态路由的关系

Not Found 在动态路由中特别常见:

/goals/[goalId]
/users/[userId]
/posts/[slug]

URL 中存在一个合法格式的参数,不代表对应资源真实存在。

所以:

Dynamic Segment
      ↓
params.goalId
      ↓
validate / query
      ↓
exists ? Page : notFound()

这和 Next.js 动态路由段 一起构成完整资源访问模型。

404 与授权不要混淆

有些系统为了避免泄露资源是否存在,会对“无权访问某资源”也返回 404,而不是 403。

这属于安全/产品策略,不能简单理解成:

没权限 = 一定 notFound()

授权判断仍然是独立问题。Route 结构、资源存在性和权限语义必须分开:

URL parameter
≠ resource exists
≠ current user may access it

与 error.tsx 的关系

Next.js Error Boundaries 负责未处理异常和局部故障隔离;本笔记负责资源不存在的 404 语义。

最短对比:

DB timeout / code failure
→ error.tsx

query succeeds, entity missing
→ notFound()
→ not-found.tsx

常见误区

所有 null 都应该 notFound()

不一定。

如果 null 表示“用户还没填写昵称”“可选关联不存在”,它可能只是正常字段状态,而不是整个 Route 的资源不存在。

只有当:

当前请求的核心资源不存在,页面本身不再有成立基础

才更适合进入 Not Found。

404 不是“服务器坏了”

它是一种明确的资源定位结果,不应该和系统异常使用同一套 UI、告警和恢复策略。

最短记忆方式

notFound()
= expected resource absence
→ not-found.tsx
→ 404 semantics

它主要回答:请求正常,但这个页面所依赖的核心资源不存在时,应该怎样准确表达?

创建于 2026/9/15 更新于 2026/9/15
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本