6.2 服务端中间件与运行时配置


6.2 服务端中间件与运行时配置

端点会写了,本节给服务端装"进站安检"与"参数面板":server/middleware 在每个请求到达端点前拦截,适合日志、CORS、鉴权预检;runtimeConfig 与环境变量让同一份产物在不同环境拿到不同配置。这两块与 5.2 的路由中间件、2.2 的配置分层遥相呼应,合起来构成完整的服务端工程面。

服务端中间件:进站第一道工序

server/middleware 下的文件对每个请求执行(包括页面请求与所有 API),且不生成路由——纯粹的路过拦截。执行顺序按文件名字典序,需要顺序时用数字前缀:

// server/middleware/1.logger.ts // 请求日志:谁、何时、要什么、多久 export default defineEventHandler((event) => { const start = Date.now() event.node.res.on('finish', () => { console.log( `[api] ${event.method} ${event.path} ` + `${event.node.res.statusCode} ${Date.now() - start}ms` ) }) })
// server/middleware/2.auth-context.ts // 鉴权预检:解析会话,塞进请求上下文,端点直接读 export default defineEventHandler(async (event) => { const token = getHeader(event, 'authorization')?.replace('Bearer ', '') // 解析结果挂在 context 上,后面的端点与中间件都能用 event.context.user = token ? await verifySession(token) // 查会话存储,返回用户或 null : null })
// server/api/orders/index.get.ts // 端点消费上下文:中间件铺好的路直接走 export default defineEventHandler((event) => { if (!event.context.user) { throw createError({ statusCode: 401, statusMessage: '请先登录' }) } return db.orders.findMany({ where: { userId: event.context.user.id } }) })

TypeScript 项目给 context 补类型声明后,event.context.user 有完整的类型提示。这套"中间件解析、端点消费"的模式把鉴权从每个端点里抽走,端点只关心业务。

CORS 也是标准中间件场景——不过 Nitro 内置了配置化方案,优先用它:

// nuxt.config.ts export default defineNuxtConfig({ nitro: { routeRules: { '/api/public/**': { cors: true }, // 这组接口允许跨域 }, }, })

与路由中间件的分工

两类中间件名字像,职责隔着一层接力棒:

维度 路由中间件(5.2) 服务端中间件(本节)
位置 middleware 目录 server/middleware 目录
拦截对象 页面导航(首航 + 换页) 一切请求(页面 + API + 静态)
运行端 服务端首航、浏览器换页 只在服务端
典型用途 登录守卫、权限改道 日志、CORS、鉴权预检、安全头
能否挡页面 能(navigateTo 改道) 能(直接返回响应短路)

一句话分工:路由中间件管"用户能去哪个页面",服务端中间件管"请求能不能进来"。安全敏感的鉴权必须两层都做——前端守卫挡误入,服务端校验挡绕过(用户可以绕过页面导航,绕不过 HTTP 请求本身)。

运行时配置:服务端的正确读法

2.2 讲过 runtimeConfig 的结构,这里落到服务端代码。三条纪律:

纪律一:密钥只在服务端读

// server/api/admin/stats.get.ts export default defineEventHandler((event) => { const config = useRuntimeConfig(event) // server-only 区域:数据库密码、第三方私钥放这里 const stats = await fetchAdminStats(config.dbPassword) // public 区域的值服务端也能读(比如拼接口地址) const base = config.public.apiBase return { stats, base } })

纪律二:环境变量覆盖,产物不变。构建一次、多环境部署的核心机制:

# 预发环境启动 NUXT_DB_PASSWORD=staging-pass node .output/server/index.mjs # 生产环境启动(同一份产物) NUXT_DB_PASSWORD=prod-pass node .output/server/index.mjs

纪律三:别把环境差异编译进产物import.meta.env.XXX 这类构建期内联的值,改一次要重新构建;runtimeConfig 是运行时读取,环境变量注入即生效。需要多环境切换的值一律走 runtimeConfig。

Nitro storage:跨请求的合法缓存

第 4 章警告过"进程级变量跨用户污染",正解在这里:Nitro 内置 storage 抽象,跨请求、跨部署形态的安全存取:

// server/api/featured.get.ts // 热门商品:五分钟缓存,过期重算 export default defineEventHandler(async () => { const storage = useStorage('cache') let featured = await storage.getItem('featured-products') if (!featured) { featured = await computeFeatured() // 昂贵的计算或查询 await storage.setItem('featured-products', featured, { ttl: 300 }) } return featured })

storage 后端可插拔:开发模式用内存,生产配 Redis 时多实例共享缓存,配置进 nitro.storage 项。它与 3.3 的 swr 缓存互补:swr 缓存的是整页 HTML,storage 缓存的是业务数据块——两层缓存搭配,服务器的重复劳动降到最低。

⚠️ 常见坑:服务端中间件里做重活拖慢全站。每个请求都过中间件,一次两百毫秒的远程校验等于全站延迟加两百毫秒。重逻辑挪进端点或用 storage 缓存结果,中间件只做轻量解析。

💡 关键直觉:把服务端中间件想成机场安检(人人过、快进快出),路由中间件想成登机口检票(只管本航班)——安检在前、检票在后,缺一不可但职责不混。

本节要点回顾

  • 中间件职责:日志、CORS、鉴权预检、安全头;数字前缀控制顺序;解析结果挂 event.context 供端点消费;
  • 与路由中间件分工:一个管页面导航、一个管一切请求;安全校验必须两层设防;
  • 配置三纪律:密钥服务端读、环境变量覆盖产物不变、多环境值走 runtimeConfig 不做构建期内联;
  • storage 是合法缓存:跨请求跨实例,后端可插拔;与 swr 的页面缓存分层互补;
  • 中间件要轻:每请求都跑的代码,重逻辑必须缓存或下沉到端点。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U