Nuxt:routeRules.proxy、server/middleware 与 server/api 的请求链路

最后更新:

本文记录一次真实项目(Nuxt 4 管理后台 + HttpOnly Cookie 会话 + 同源 /api/admin/**)中的鉴权代理问题排查结论,便于以后选型时不再踩「routeRules.proxy 看起来省事,但 Cookie 转不了 Authorization」的坑。

三种机制各自干什么

机制 配置位置 谁在执行 典型用途
routeRules.proxy nuxt.config.ts Nitro 内置反向代理(偏底层) 零代码把某路径原样转到外部 URL
server/middleware/* server/middleware/ Nitro 服务端中间件 改请求头、日志、鉴权注入(前提:请求先进入 Nitro 常规管线
server/api/* server/api/ Nitro 文件路由(BFF / 自定义代理) 登录 BFF、带逻辑的 API 转发、改响应体

另外还有 middleware/*.global.tsapp/middleware:这是 Vue 路由中间件,只管页面导航(例如 /console/login),不参与 /api/admin/** 的 HTTP 代理。

调试前的设计:routeRules.proxy

// nuxt.config.ts(问题版本,已移除)
routeRules: {
  '/api/admin/**': {
    proxy: `${NUXT_API_PROXY_TARGET}/api/admin/**`
  }
}

浏览器请求过程(简化):

sequenceDiagram
  participant Browser
  participant Nuxt as Nuxt_Nitro
  participant Proxy as routeRules_proxy
  participant Backend as 真实后端

  Browser->>Nuxt: GET /api/admin/menus/user + Cookie
  Note over Nuxt: 期望 server/middleware 注入 Authorization
  Nuxt->>Proxy: 命中 routeRules.proxy
  Proxy->>Backend: 透明转发(常不带 Authorization)
  Backend-->>Browser: 401 未登录或 Token 已过期

日志里证实的问题

结论:routeRules.proxy 是透明转发,不会走 server/api/admin/[...path].ts,也不能指望 server/middleware 在代理前稳定地把 Cookie 转成 Authorization

登录类请求:一直用 server/api

sequenceDiagram
  participant Browser
  participant BFF as server_api_auth
  participant ServerApi as createServerApi
  participant Backend as 真实后端

  Browser->>BFF: POST /api/auth/login
  BFF->>ServerApi: 调 dingtalk/login 等
  ServerApi->>Backend: 由 BFF 处理鉴权
  Backend-->>BFF: access_token + user
  BFF->>Browser: Set-Cookie oa_auth + 只返回 user

调试后的业务 API:server/api/admin/[...path].ts

去掉 routeRules/api/admin/** 代理后,由 catch-all 路由 接管:

sequenceDiagram
  participant Browser
  participant Handler as server_api_admin_path
  participant Ky as createAdminProxyClient
  participant Backend as 真实后端

  Browser->>Handler: GET /api/admin/menus/user + Cookie
  Handler->>Handler: getAuthHeaderFromEvent(event)
  Note over Handler: Cookie 解析 + Bearer 规范化
  Handler->>Ky: ky.get('menus/user')
  Ky->>Backend: Authorization: Bearer xxx
  Backend-->>Handler: 200 + JSON
  Handler-->>Browser: 原样透传 body/status

关键实现点:

server/middleware/proxy-auth.ts 可保留作补充(给仍走 event.node.req/api/admin 请求写 authorization),但 可靠路径是 handler + createAdminProxyClient

页面侧:app/middleware + CSR

flowchart LR
  A[访问 /console/*] --> B[middleware/console-auth.global.ts]
  B -->|CSR| C[me 校验会话]
  B -->|SSR| D[只检查 oa_auth Cookie 是否存在]
  C --> E[console layout onMounted]
  E --> F[getCurrentUserMenus via useClientApi]
  F --> G[/api/admin/menus/user]
  G --> H[server/api/admin handler]

注意:

对比:该用哪种方式

需求 routeRules.proxy server/middleware server/api
零代码转发 适合 单独不够 可选
Cookie → Authorization 本次不可靠 仅当请求走常规 Nitro 管线 推荐
改登录响应 / 写 Cookie server/api/auth
统一透传 body/status [...path].ts
useClientApi 同源 /api/admin URL 可匹配 依赖是否进管线 明确命中

不要两套代理同时开

若同时保留:

routeRules: { '/api/admin/**': { proxy: '...' } }

和:

server/api/admin/[...path].ts

可能出现谁先匹配不确定、行为难排查。业务 API 应 只保留 handler 方案

若坚持只用 routeRules.proxy

需要另找能在代理前注入鉴权的方式;按本次调试,仅靠 server/middleware + routeRules.proxy 不可靠。更稳妥的是:

一句话结论

  1. routeRules.proxy:适合纯静态转发、不涉及会话改造的接口;不适合「HttpOnly Cookie → 后端 Bearer」的 BFF 场景。
  2. server/middleware:是 Nitro 钩子,不能假设所有 /api/** 都会经过它;routeRules.proxy 往往会绕开。
  3. server/api:适合登录 BFF + 需要鉴权注入的业务代理/api/auth/*/api/admin/**[...path].ts)是正确分层。
  4. routeRules 仍然有用:继续管页面级规则(如 '/'prerender、登录页 ssr: false 等),不要再和业务 /api/admin/**proxy 混用。

相关环境变量(oa-web)

变量 作用
NUXT_PUBLIC_API_BASE_URL 前端请求前缀,一般为 /api/admin/
NUXT_API_PROXY_TARGET 服务端转发真实后端根地址

浏览器只访问同源 /api/admin/**;Nitro 在服务端把 Cookie 转为 Authorization 后转发到真实后端。