React Router中间件与Context API入门:像Express一样搞定鉴权、日志与错误处理
【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router
对于使用 React Router 做声明式路由的 React 开发者来说,React Router 中间件(Middleware)和Context API是 v7/v8 版本带来的重磅能力:它让鉴权拦截、请求日志、错误处理这些曾经需要外部 HTTP 服务器(如 Express)才能优雅完成的工作,直接在路由层以复用、类型安全的方式搞定。本文将从零讲透这两个概念,帮你快速在 React 应用中搭起一条完整的请求处理链路。
什么是 React Router 中间件?
如果你用过 Express,那么 React Router 中间件的心智模型几乎一模一样:
- 洋葱模型执行:请求"向下"时,从根路由 → 父路由 → 子路由依次执行中间件;
Response生成后"向上"原路返回,父级中间件可以基于最终响应做收尾处理 - 一个
next()函数:调用它才会执行下一个中间件或路由的loader/action;你不需要try/catch包裹它,内部错误会被路由的ErrorBoundary统一接管 - 按路由挂载:中间件以数组形式挂在具体路由上,天然支持"整棵子树共用一套拦截逻辑",比如给
dashboard布局路由挂一个鉴权中间件,所有子页面自动受保护
GET /parent/child 的执行顺序: Root 中间件开始 → Parent 中间件开始 → Child 中间件开始 → 执行 loaders、生成 Response → Child 中间件结束 → Parent 中间件结束 → Root 中间件结束官方把执行时机讲得非常清楚:服务端中间件优先遵循 SPA 行为,只包裹真实发生的请求,避免无意义的服务器往返;客户端中间件则在每次客户端导航时都会运行。详见 docs/how-to/middleware.md。
快速上手:三步配置 React Router 中间件
第一步:用 createContext 创建类型安全的 Context
Context 是中间件向下游(其他中间件、loader、action)传递数据的"容器",用法类似 React 的createContext,但专为请求/响应生命周期设计:
import { createContext } from "react-router"; export const userContext = createContext<User | null>(null);API 定义位于 packages/react-router/lib/router/utils.ts,提供了context.set()/context.get()两个方法,值类型由泛型约束,从中间件到 loader 全程类型安全。
第二步:在路由上导出中间件
export const middleware: Route.MiddlewareFunction[] = [authMiddleware]; // loader 中直接取用中间件塞进去的值 export async function loader({ context }: Route.LoaderArgs) { const user = context.get(userContext); return { profile: await getProfile(user) }; }仓库内置了一个可以直接跑的演示工程 playground/middleware/,其中 playground/middleware/app/root.tsx 展示了根路由如何同时挂载服务端中间件和客户端中间件,playground/middleware/app/contexts.ts 则是几个典型 Context 定义,值得逐行读一遍。
第三步:需要全局注入时用 getLoadContext
如果你使用自定义服务器,可以提供一个getLoadContext函数,用RouterContextProvider把数据库连接等共享值预先塞进 Context,整条中间件链都能拿到。
三大高频场景实战
场景一:鉴权拦截与登录跳转
这是中间件最经典的用途。在鉴权中间件里检查会话,未登录直接throw redirect("/login"),登录成功后context.set(userContext, user),下游所有 loader 都能拿到"已认证"的用户,无需重复查询:
export const authMiddleware = async ({ request, context }) => { const user = await getUserFromSession(request); if (!user) throw redirect("/login"); context.set(userContext, user); }; // 只需前置逻辑时,可省略 next(),框架会自动调用场景二:请求日志与耗时统计
在next()前记录请求开始时间与requestId,在next()后计算耗时并打印状态码,一条链路即可实现带追踪 ID 的完整访问日志。日志中间件的完整写法见 docs/how-to/middleware.md 的 "Logging" 小节。
场景三:错误处理与安全响应头
两个容易忽略但很实用的模式:
- 兜底错误处理:中间件内抛出的错误会被最近的
ErrorBoundary捕获,并通过祖先的next()正常返回响应,所以你不需要手动 try/catch,根中间件里的收尾逻辑(比如提交会话 Cookie)依然会执行 - 统一安全头:在根路由中间件的
next()之后给响应追加X-Frame-Options、X-Content-Type-Options等头,全站一次配置生效
此外还支持"404 时查 CMS 重定向""条件执行中间件(如仅对 POST 鉴权)"等模式,仓库 decisions/0014-context-middleware.md 记录了这套 API 从设计到定稿的完整思考过程,想深入理解"为什么这样设计"可以一读。
服务端与客户端中间件:一个 API,两种运行环境
| 对比项 | 服务端中间件(middleware) | 客户端中间件(clientMiddleware) |
|---|---|---|
| 运行位置 | 服务器(Framework 模式) | 浏览器(Framework / Data 模式) |
| 输入 | 真实 HTTPRequest | 导航请求对象 |
| 返回值 | 必须把Response沿链路上抛 | next()无 Response 可上抛,通常忽略返回值 |
| 典型用途 | 鉴权、日志、安全头、会话 | 导航耗时打点、404 兜底跳转 |
客户端中间件在拿不到 Response 的场景下,next()会回传各路由 loader/action 的执行结果,你可以据此做条件处理——官方文档给了一个"检测到 404 就查 CMS 重定向表"的经典例子。
关键资料索引:快速定位文档与源码
- 📘 官方中间件指南(推荐精读):docs/how-to/middleware.md
- 📘
createContextAPI 文档:docs/api/utils/createContext.md - 📘 路由对象写法参考:docs/start/framework/route-module.md
- 📖 架构决策记录(中间件设计背景):decisions/0014-context-middleware.md
- 🔍 核心 API 实现:packages/react-router/lib/router/utils.ts
- 🧪 可运行演示工程:playground/middleware/
写在最后
React Router 中间件 + Context API 的组合,等于在 React 世界里复刻了 Express 那套熟悉的请求处理范式:鉴权、日志、错误兜底、响应头治理,全部收敛到路由这一层。上手门槛极低(三步配置),收益却很高——共享逻辑写一次、按路由树挂载、全程类型安全。建议先跑通 playground/middleware/ 的演示,再对照官方文档把三大场景套进自己的项目,你会发现"像 Express 一样写 React 应用"真的不难 😎
【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考