Vercel Serverless Functions 进阶配置用vercel.json构建企业级 API 网关如果你正在使用 Vercel 部署你的前端应用那么 Serverless Functions 大概率是你处理后端逻辑的首选。它简单、快速而且与前端项目天然集成。但当你开始构建更复杂的应用尤其是需要处理多个 API 端点、统一的跨域策略或清晰的版本管理时你是否发现自己在每个函数里重复写着几乎相同的 CORS 头或者当 API 需要升级时如何优雅地处理新旧版本共存的问题而不至于让客户端开发者抓狂这正是vercel.json这个看似简单的配置文件大显身手的地方。它远不止是一个部署说明文件而是一个强大的边缘网络配置中心。通过它你可以在请求真正触达你的 Serverless Function 代码之前就在全球分布的 CDN 边缘节点上完成一系列关键操作。这意味着你可以将那些通用、繁琐但又至关重要的逻辑从函数运行时中剥离出来交给更高效、成本更低的边缘网络去处理。今天我们就来深入探讨如何将vercel.json中的routes配置打造成你专属的、轻量级的 API 网关一站式解决 CORS、API 版本控制、请求重写等常见痛点让你的 Serverless 架构更加清晰和健壮。1. 理解vercel.json的routes你的边缘路由器在深入实战之前我们必须先理解vercel.json中routes配置的核心价值。它不是在 Node.js 运行时里执行的代码而是在 Vercel 的全球边缘网络上生效的规则。当一个 HTTP 请求到达 Vercel 时它会首先经过这个路由层。你可以把它想象成你家门口的智能信箱信件请求首先到达信箱。信箱规则routes会根据寄件人、信件类型比如账单、广告自动分拣有的直接贴上“已转发”的标签重写有的被贴上新的邮寄信息添加头信息有的则直接被退回并告知新地址重定向。最终处理过的信件才会被送到你Serverless Function手中。这种设计的优势是显而易见的性能极致添加一个 HTTP 头或者做一个简单的路径匹配在边缘网络的耗时是微秒级的远比启动一个冷启动的 Serverless Function 要快得多。成本为零这些路由规则在边缘网络执行不会触发 Serverless Function 的执行因此不会产生任何函数调用费用。配置即代码所有路由规则与你的项目代码一起版本化管理部署流程完全一致。一个基础的vercel.json结构通常如下所示我们今天的主角routes是其中的核心数组{ builds: [ { src: api/**/*.js, use: vercel/node } ], routes: [ // 我们将在这里添加所有神奇的路由规则 ] }2. 实战一全局化与精细化的 CORS 配置跨源资源共享CORS是前端开发者绕不开的话题。在 Serverless Functions 中常见的做法是在每个 API 函数的开头写一段设置 CORS 头的代码。这不仅重复而且容易出错或遗漏。2.1 使用routes实现全局 CORS最直接的方式是为所有/api路径下的请求统一添加 CORS 头。这在开发环境或公开 API 中非常有用。{ routes: [ { src: /api/(.*), headers: { Access-Control-Allow-Origin: *, Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS,PATCH, Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization, X-CSRF-Token, Access-Control-Allow-Credentials: false }, continue: true } ] }关键参数解析src: /api/(.*)这是一个正则表达式匹配所有以/api/开头的路径。(.*)捕获了api/之后的所有内容。headers指定要添加或覆盖的 HTTP 响应头。continue: true这个参数至关重要。它告诉 Vercel 在应用完当前路由规则的头信息后继续处理后续的规则或者最终将请求路由到对应的 Serverless Function。如果设置为false请求会在此处终止不会到达你的函数。注意将Access-Control-Allow-Origin设置为*通配符时Access-Control-Allow-Credentials必须为false。如果需要携带 Cookie 等凭证则必须指定明确的源Origin不能使用通配符。2.2 实现动态、安全的 CORS 策略生产环境中我们通常需要更精细的控制只允许特定的前端域名访问并且可能根据环境开发、预览、生产动态调整。虽然routes配置是静态的但我们可以通过结合环境变量和 Vercel 提供的系统环境来模拟动态行为。Vercel 会自动注入一些环境变量如VERCEL_ENV值为production,preview,development和VERCEL_URL。我们可以利用这些在部署时“生成”不同的vercel.json配置或者使用一个更巧妙的办法在src匹配中前置检查但这通常需要更复杂的逻辑。更常见的生产级做法是在routes中设置一个相对宽松但安全的头然后在 Serverless Function 中进行最终的、业务相关的 CORS 验证。不过对于允许多个特定源的情况可以在routes中预设一个头然后在函数中动态覆盖它。虽然routes不能直接进行复杂的逻辑判断但它为后续处理铺平了道路。2.3 预检请求OPTIONS的处理对于非简单请求例如携带Content-Type: application/json或自定义头浏览器会先发送一个OPTIONS方法的预检请求。我们可以在routes中直接处理它避免触发一个无用的 Serverless Function 调用。{ routes: [ { src: /api/(.*), methods: [OPTIONS], headers: { Access-Control-Allow-Origin: https://your-frontend-app.vercel.app, Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS,PATCH, Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization, Access-Control-Max-Age: 86400 }, status: 204, continue: false }, { src: /api/(.*), headers: { Access-Control-Allow-Origin: https://your-frontend-app.vercel.app, Access-Control-Allow-Credentials: true }, continue: true } ] }这个配置做了两件事第一条规则专门匹配OPTIONS方法。它设置了完整的 CORS 头并立即返回204 No Content状态码continue: false确保请求到此结束不会去执行函数。Access-Control-Max-Age告诉浏览器可以将这个预检结果缓存 24 小时减少重复的预检请求。第二条规则匹配所有其他方法的/api请求设置允许的源和凭证标志。continue: true让请求带着这些头信息继续流向目标函数。通过这种方式预检请求被高效地拦截在边缘层节省了函数执行的开销和延迟。3. 实战二优雅的 API 版本控制策略API 版本管理是服务演进中的关键。粗暴地直接修改现有接口会破坏现有客户端。常见的版本控制方式有URL 路径版本化/api/v1/users,/api/v2/users请求头版本化Accept: application/vnd.myapp.v1json查询参数版本化/api/users?version1其中URL 路径版本化最为直观和常见。vercel.json的routes可以完美地支持这种模式实现请求的透明重写。3.1 使用重写Rewrite进行版本路由假设你的项目目录结构如下/api/ ├── v1/ │ └── user.js (处理 /api/v1/user) └── v2/ └── user.js (处理 /api/v2/user)你的函数文件api/v2/user.js内部可能监听的是根路径。但你可以通过routes配置将客户端对/api/v2/user的请求无缝地“重写”到/api/v2/user这个函数路径在这个例子中路径一致但重写机制可以处理更复杂的情况。更重要的是你可以用它来实现默认版本和版本别名。{ routes: [ { src: /api/users, dest: /api/v2/users }, { src: /api/posts, dest: /api/v1/posts } ] }这个配置意味着当客户端访问https://yourapp.vercel.app/api/users时Vercel 边缘网络会将其内部重写为/api/v2/users然后执行对应的api/v2/users.js函数。客户端完全感知不到这个变化。访问/api/posts则被重写到v1版本。这带来了巨大的灵活性设置默认版本你可以将最新的、推荐的 API 版本设置为默认路径降低客户端的升级成本。逐步迁移你可以将部分端点指向新版本部分保留旧版本进行灰度发布。路径简化对外提供简洁的 API 路径内部通过重写映射到具体的版本化函数文件。3.2 使用重定向Redirect处理废弃版本当某个旧版本 API 被彻底废弃时你应该使用301永久重定向或308状态码来告知客户端和搜索引擎资源已永久迁移。这比直接返回410 Gone或404 Not Found更友好。{ routes: [ { src: /api/v1/users, status: 308, headers: { Location: /api/v2/users } }, { src: /api/v1/posts/(.*), status: 301, destination: /api/v2/posts/$1 } ] }参数对比特性重写 (dest)重定向 (statusLocation/destination)行为内部转发客户端URL不变服务器告知客户端跳转到新URL客户端URL改变状态码保持原始状态码如2003xx (301, 302, 308等)适用场景版本路由、路径映射、代理废弃接口迁移、旧链接跳转、SEO优化客户端感知无感知感知到URL变化在上面的例子中对/api/v1/posts/123的请求浏览器会收到一个301响应并自动跳转到/api/v2/posts/123。$1用于捕获(.*)匹配到的内容即123并在新路径中复用。4. 实战三组合技与高级路由模式单一规则能解决的问题有限但将多条routes规则组合起来就能构建出非常强大的请求处理流水线。规则的执行顺序就是它们在数组中的定义顺序。4.1 构建一个完整的 API 网关配置让我们综合运用 CORS、版本控制和静态资源处理为一个假设的项目创建一个完整的vercel.json配置。{ builds: [ { src: api/**/*.js, use: vercel/node }, { src: public/**/*, use: vercel/static } ], routes: [ // 1. 处理 API 预检请求 (最高优先级) { src: /api/(.*), methods: [OPTIONS], headers: { Access-Control-Allow-Origin: https://www.myapp.com, Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS,PATCH, Access-Control-Allow-Headers: Content-Type, Authorization, X-Client-Version, Access-Control-Max-Age: 86400 }, status: 204, continue: false }, // 2. 为所有 API 请求添加安全头和 CORS 头 { src: /api/(.*), headers: { Access-Control-Allow-Origin: https://www.myapp.com, Access-Control-Allow-Credentials: true, X-Content-Type-Options: nosniff, X-Frame-Options: DENY }, continue: true }, // 3. API 版本路由和重定向 { src: /api/legacy/users, status: 301, destination: /api/v1/users }, { src: /api/user, // 默认路径指向最新稳定版 dest: /api/v2/user }, { src: /api/users, // 复数路径也指向最新版 dest: /api/v2/users }, // 4. 代理特定请求到外部服务例如处理支付回调 { src: /api/webhook/stripe, dest: https://my-secure-backend.example.com/webhooks/stripe, headers: { X-Forwarded-Host: www.myapp.com } }, // 5. 前端 SPA 路由回退必须放在最后 { src: /(.*), dest: /index.html } ] }4.2 调试与验证你的路由配置配置复杂的routes后如何验证它们按预期工作以下是一些实用技巧使用vercel dev本地测试在项目根目录运行vercel devVercel CLI 会模拟生产环境的路由行为。你可以使用curl或 Postman 直接测试本地服务器的响应头。# 测试预检请求 curl -X OPTIONS http://localhost:3000/api/users -v # 测试重写 curl http://localhost:3000/api/user -v # 测试重定向 curl -L http://localhost:3000/api/legacy/users -v检查部署后的日志在 Vercel 项目的 Dashboard 中查看 “Functions” 标签页下的日志。如果请求被routes规则拦截如预检请求返回 204则不会出现在函数调用日志中。如果请求成功到达函数你可以看到对应的执行日志。浏览器开发者工具在 Network 面板中仔细查看请求和响应的头信息Headers特别是Access-Control-Allow-*系列头和Location用于重定向。5. 超越routes与 Serverless Functions 内部逻辑的协作vercel.json的routes配置虽然强大但它主要在 HTTP 协议层工作。许多业务逻辑如 JWT 令牌验证、请求体解析、数据库操作、复杂的错误处理等仍然需要在 Serverless Function 内部完成。5.1 中间件模式高阶函数这是 Serverless 架构下实现可复用逻辑的经典模式。你创建一些包装函数高阶函数它们接收你的业务处理函数并返回一个新的函数在新函数中添加通用逻辑。// lib/middleware.js export const withAuth (handler) async (req, res) { const token req.headers.authorization?.replace(Bearer , ); if (!token) { return res.status(401).json({ error: Missing authentication token }); } try { // 假设我们有一个验证 JWT 的异步函数 const user await verifyJWT(token, process.env.JWT_SECRET); req.user user; // 将用户信息附加到请求对象 return handler(req, res); } catch (error) { return res.status(403).json({ error: Invalid or expired token }); } }; export const withValidation (schema) (handler) async (req, res) { try { // 使用 Joi、Yup 或 Zod 等库进行验证 const validatedData await schema.validateAsync(req.body, { abortEarly: false }); req.validatedBody validatedData; return handler(req, res); } catch (validationError) { return res.status(422).json({ error: Validation failed, details: validationError.details }); } };在 API 函数中使用它们// api/v2/protected/profile.js import { withAuth, withValidation } from ../../lib/middleware; import { updateProfileSchema } from ../../lib/schemas; const updateProfileHandler async (req, res) { // 此时req.user 和 req.validatedBody 已经由中间件提供 const userId req.user.id; const updateData req.validatedBody; // ... 业务逻辑如更新数据库 res.status(200).json({ message: Profile updated, userId, updateData }); }; // 组合中间件先认证再验证最后执行业务逻辑 export default withAuth(withValidation(updateProfileSchema)(updateProfileHandler));5.2 与routes配置的分工现在我们可以清晰地划分vercel.json的routes和 Serverless Function 内部中间件的职责关注点推荐实现位置理由CORS 头设置vercel.jsonroutes性能最优零成本尤其适合处理 OPTIONS 预检请求。API 版本路由/重定向vercel.jsonroutes在边缘层完成路径映射逻辑清晰易于管理。添加安全头HSTS, CSP等vercel.jsonroutes或headers全局应用无需业务代码干预。JWT 认证/授权Serverless Function 中间件需要业务逻辑如查询数据库验证用户状态。请求体验证Serverless Function 中间件需要解析 JSON 并根据业务规则进行复杂验证。日志记录Serverless Function 中间件需要记录业务相关的上下文信息。错误统一处理Serverless Function 中间件需要根据业务错误类型返回不同的 HTTP 状态码和消息。数据库连接池管理Serverless Function 初始化属于运行时资源管理。这种分工协作的模式使得你的 Serverless 应用既拥有了边缘网络带来的速度和成本优势又保持了业务逻辑的灵活性和完整性。vercel.json成为了你应用架构中一个强大的、声明式的配置层而你的函数代码则可以更专注于实现核心业务价值。