remix 的 cors-middleware 完全指南:从预检短路到动态 Origin 策略的 Fetch API 跨域方案
发布时间:2026/9/11 11:06:34 作者:尧图编辑部 阅读量:1,286

remix 的 cors-middleware 完全指南从预检短路到动态 Origin 策略的 Fetch API 跨域方案【免费下载链接】remixThe fully-stacked web framework项目地址: https://gitcode.com/GitHub_Trending/re/remix导读cors-middleware是 remix 仓库中面向 Fetch API 服务器的标准 CORS 中间件负责为响应附加标准跨域头并智能处理OPTIONS预检请求——既可短路预检也可透传给应用自定义的OPTIONS处理器。读完本文你将掌握它的全部配置项Origin 匹配、凭据、暴露头、私有网络预检等理解预检短路的底层实现原理并能直接在基于fetch-router的 API 服务中落地可复制的跨域方案。功能总览根据 packages/cors-middleware/README.md该中间件提供以下核心能力预检处理Preflight Handling自动处理OPTIONS预检请求灵活的 Origin 规则Flexible Origin Rules支持静态字符串、正则、数组与函数四种 Origin 策略凭据支持Credential Support支持携带凭据的请求并做符合规范的 Origin 反射请求头控制Header Controls可配置允许头、暴露头、预检方法与缓存时长私有网络支持Private Network Support可选地放行私有网络预检请求。安装方式它是 remix monorepo 中的一个 workspace 包完整安装 remix 即可使用npm i remix该包的清单文件见 packages/cors-middleware/package.json发布名为remix-run/cors-middleware导出入口为src/index.ts源码位于 packages/cors-middleware/src/lib/cors.ts。快速上手中间件与fetch-router的createRouter直接组合在middleware数组中注册即可生效import { createRouter } from remix/router import { cors } from remix/middleware/cors let router createRouter({ middleware: [ cors({ origin: [https://app.example.com, https://admin.example.com], credentials: true, exposedHeaders: [X-Request-Id], }), ], }) router.get(/api/projects, () { return Response.json([{ id: p1, name: Remix }], { headers: { X-Request-Id: req_123, }, }) })启动后所有响应都会附带与请求匹配的Access-Control-*响应头浏览器跨域调用即可正常读取数据与自定义响应头。Origin 策略Origin Policiesorigin配置项支持以下全部取值形式对应源码中的CorsOrigin类型见 cors.ts取值含义*允许所有来源string单个精确 Origin必须与请求Origin完全相等才放行RegExp基于正则的模式匹配Arraystring \| RegExp多个精确值与模式的混合匹配true反射请求 Origin把请求的Origin原样写回响应头false完全禁用 CORS 响应头返回null等价于不匹配(origin, context) boolean \| string动态策略函数限制来源Restrict Origins用数组精确列举可信来源是 API 服务的常见做法let router createRouter({ middleware: [ cors({ origin: [https://app.example.com, https://admin.example.com], credentials: true, }), ], })从源码resolveAllowedOrigincors.ts可以看到匹配逻辑字符串要求严格相等正则通过RegExp.test判断数组逐项遍历命中即返回请求 Origin全部未命中返回null此时预检请求会被中间件以403短路拒绝对应测试 cors.test.ts。动态 Origin 策略Dynamic Origin Policies当允许策略依赖请求上下文例如路径、请求头时使用函数形式let router createRouter({ middleware: [ cors({ origin(origin, context) { if (context.url.pathname.startsWith(/public/)) { return * } return origin.endsWith(.trusted.example) }, }), ], })函数接收两个参数请求的Origin字符串与RequestContext。从源码看context提供了headers、url、method、request等字段request-context.ts。函数返回值经过normalizeResolvedOrigincors.ts归一化true反射请求 Origin、*通配、false/null/undefined表示拒绝、字符串作为精确值输出返回 Promise 同样支持。测试用例 cors.test.ts 验证了origin.endsWith(.trusted.example)的动态匹配行为。一个值得注意的细节正则匹配时源码会基于pattern.source与pattern.flags重建一个新的RegExp实例见matchesOriginPattern因此即使配置了带g标志的正则跨多次请求也能保持一致匹配结果不会因 lastIndex 状态残留而失效测试 cors.test.ts 专门覆盖了这一点。预检行为Preflight Behavior默认短路204默认情况下预检请求会被中间件短路返回状态码204无响应体let router createRouter({ middleware: [ cors({ methods: [GET, POST, PATCH], allowedHeaders: [Authorization, Content-Type], maxAge: 600, }), ], })相关配置项methods预检响应的Access-Control-Allow-Methods默认值为[GET, HEAD, PUT, PATCH, POST, DELETE]见 cors.ts。注意配置会被统一转换为大写并去重normalizeMethodList。allowedHeaders预检响应的Access-Control-Allow-Headers。未配置时默认反射请求携带的Access-Control-Request-Headers见 cors.ts。maxAgeAccess-Control-Max-Age秒源码会执行Math.max(0, Math.floor(options.maxAge))归一化为非负整数后才写入响应头。中间件判断预检请求的条件是context.method OPTIONS且请求头存在Access-Control-Request-Method见isPreflightRequestcors.ts。预检响应会同时设置Vary: Access-Control-Request-Method确保缓存正确区分不同方法的预检结果。基于请求的 allowedHeaders 策略当允许头列表需要随请求动态变化时传入函数let router createRouter({ middleware: [ cors({ allowedHeaders(request) { let requestedHeaders request.headers.get(Access-Control-Request-Headers) if (requestedHeaders?.includes(x-admin-token)) { return [Authorization, Content-Type, X-Admin-Token] } return [Authorization, Content-Type] }, }), ], })函数接收Request与RequestContext两个参数可同步或异步返回string[]。基于函数的allowedHeaders响应随Access-Control-Request-Headers变化因此中间件会自动为这类响应附加Vary: Access-Control-Request-Headers缓存不会复用不同请求头集合下的预检响应对应源码varyOnRequestHeaders逻辑与测试 cors.test.ts。若函数返回null/undefined则回退为反射浏览器请求的头列表测试见 cors.test.ts。透传与自定义状态码设置preflightContinue: true后预检请求不再被短路而是继续进入下游处理器例如你自行注册的router.options(...)处理器此时 CORS 响应头仍会附加到最终响应上。测试 cors.test.ts 验证了preflightContinue透传后仍保留Access-Control-Allow-Origin与Access-Control-Allow-Methods。使用preflightStatusCode可改变短路预检的响应状态码默认204。该配置同样作用于请求无Origin头但为预检请求的情况见 cors.ts。私有网络预检Private Network Preflightslet router createRouter({ middleware: [ cors({ allowPrivateNetwork: true, }), ], })启用allowPrivateNetwork后当预检请求携带Access-Control-Request-Private-Network: true时中间件会在响应中附加Access-Control-Allow-Private-Network: true源码见 cors.ts同时把Access-Control-Request-Private-Network加入Vary。该行为由测试 cors.test.ts 验证。这适用于本地开发联调、内网管理后台等需要从浏览器访问私有网络资源的场景。暴露响应头Expose Response Headers默认情况下浏览器跨域环境下 JS 只能读取 CORS-safelisted 响应头自定义响应头如X-Request-Id、X-Trace-Id必须通过exposedHeaders显式暴露let router createRouter({ middleware: [ cors({ exposedHeaders: [X-Request-Id, X-Trace-Id], }), ], })从源码看cors.tsexposedHeaders只作用于实际请求非预检生成Access-Control-Expose-Headers响应头配置头名会经过去空白与大小写去重normalizeHeaderList。测试 cors.test.ts 验证了输出格式X-Request-Id, X-Trace-Id。源码实现要点与缓存安全理解底层实现有助于在生产环境正确使用请求处理顺序cors.ts中间件先读取请求Origin无Origin头时直接放行预检请求除外解析允许 Origin 失败时预检短路403普通请求放行匹配成功后构造 CORS 头预检请求短路或继续普通请求调用next()后把 CORS 头合并进响应。凭据与通配的冲突处理当credentials: true与origin: *同时出现时Access-Control-Allow-Origin不会被写成*浏览器禁止凭据模式下的通配而是反射请求 Origin并附加Vary: Origin保证缓存安全见 cors.ts。Vary 合并机制中间件基于remix-run/headers/vary提供的Vary类见 packages/headers/src/lib/vary.ts维护去重、小写归一化的Vary集合若下游响应本身已带Vary如Accept-EncodingwithCorsHeaders会将其与 CORS 相关 Vary 合并输出测试 cors.test.ts。响应构造短路响应通过new Response(null, { status, headers })生成合并响应则基于原响应重建Response保留status、statusText与响应体。注意事项CaveatsCORS 本质是浏览器侧的强制机制被拒绝来源的非预检请求例如简单请求仍然会到达你的处理器。若 API 需要真正拒绝跨域调用必须在处理器内部另行校验Origin不能只依赖 CORS 头。credentials: true与origin: *组合时中间件反射请求 Origin 并附加Vary: Origin确保缓存安全。allowedHeaders为函数时预检响应会基于Access-Control-Request-Headers变化附加对应 Vary避免缓存错配。preflightContinue与preflightStatusCode只影响预检OPTIONS请求的处理方式不改变实际请求的鉴权逻辑。相关包与延伸阅读本包依赖fetch-routerFetch API 路由核心packages/fetch-router与headers类型化 HTTP 头工具packages/headers可结合以下仓库内容深入理解cop-middleware针对不安全跨源请求的浏览器来源保护中间件fetch-routerFetch API 路由器headers类型化 HTTP 头工具其中的Vary类是 CORS 缓存安全的关键基础设施。CORS 协议本身的规范参考包括 MDN 的 Cross-Origin Resource Sharing 文档、WHATWG Fetch Standard 的 CORS protocol 章节以及 expressjs/cors、rack-cors 等社区实现本文不再展开外部链接。本包遵循 MIT 许可许可文本见仓库根目录 LICENSE。总结cors-middleware把 CORS 预检协议中最繁琐的部分预检识别、Origin 匹配、凭据反射、Vary 缓存安全、私有网络预检封装为一个声明式的cors()中间件与fetch-router组合即可为 Fetch API 服务提供完整的跨域能力。无论你的场景是白名单 Origin、动态策略、携带凭据的会话 API还是内网联调都可以在 packages/cors-middleware/src/lib/cors.ts 与 packages/cors-middleware/src/lib/cors.test.ts 中找到对应的实现与测试依据按需裁剪配置即可。【免费下载链接】remixThe fully-stacked web framework项目地址: https://gitcode.com/GitHub_Trending/re/remix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考