资讯详情

Wasp 中间件配置实战:全局、API 级与路径级自定义 Express 中间件

📅 2026/9/15 3:19:12 | 华诺云谱 👁 阅读
Wasp 中间件配置实战:全局、API 级与路径级自定义 Express 中间件
Wasp 中间件配置实战全局、API 级与路径级自定义 Express 中间件【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/waspWasp 框架内置了一套开箱即用的 Express 中间件Helmet、CORS、Morgan、JSON 解析、URL-encoded 解析与 Cookie 解析但真实业务中往往需要按需增删改为 CORS 添加多个允许域名、为 webhook 回调关闭 JSON 解析、为某个路径下的所有接口统一挂载自定义逻辑。本指南以 Wasp 0.15 的middleware-config文档为主线结合仓库中代码生成器的真实模板实现系统讲解 Wasp 中间件配置的三种层级全局、api级、路径级及其底层原理读完即可在自己的 Wasp 应用中精准定制 Express 中间件链。Wasp 默认自带哪些全局中间件Wasp 为每个应用生成的 Express 服务器默认挂载以下中间件详见生成模板中的defaultGlobalMiddlewareConfig中间件作用说明Helmet通过设置各类 HTTP 响应头提升安全性不是银弹但是一个很好的起点CORS启用跨域资源共享CORS支持各种选项⚠️ 前端与后端通信必须依赖该中间件MorganHTTP 请求日志中间件开发模式默认使用logger(dev)express.json解析请求体基于 body-parser结果挂到req.body⚠️ Operationsquery/action正常工作必须依赖JSON 解析express.urlencoded仅解析Content-Type匹配的 urlencoded 请求体基于 body-parsercookieParser解析Cookie请求头按 cookie 名填充req.cookies对象—这套默认组合对大多数用户是够用的但 Wasp 也意识到部分用户希望添加、修改或移除某些中间件——既可以全局生效也可以针对单个api或某个路径生效。默认中间件定义一个可覆盖的 Map在配置自定义中间件之前先了解默认配置在代码中的真实形态。在main.wasp或src/serverSetup等实现文件中可以引用到如下定义它就是你可以覆盖的默认值const defaultGlobalMiddleware new Map([ [helmet, helmet()], [cors, cors({ origin: config.allowedCORSOrigins })], [logger, logger(dev)], [express.json, express.json()], [express.urlencoded, express.urlencoded({ extended: false })], [cookieParser, cookieParser()] ])TypeScript 版本带有完整类型标注export type MiddlewareConfig Mapstring, express.RequestHandler // 下面所有示例都会用到 export type MiddlewareConfigFn (middlewareConfig: MiddlewareConfig) MiddlewareConfig const defaultGlobalMiddleware: MiddlewareConfig new Map([ [helmet, helmet()], [cors, cors({ origin: config.allowedCORSOrigins })], [logger, logger(dev)], [express.json, express.json()], [express.urlencoded, express.urlencoded({ extended: false })], [cookieParser, cookieParser()] ])这些类型的 SDK 侧定义可以在 wasp/server/middleware 的 SDK 模板中看到// PUBLIC API export type MiddlewareConfigFn (middlewareConfig: MiddlewareConfig) MiddlewareConfig // PRIVATE API export type MiddlewareConfig Mapstring, RequestHandler理解这个结构是掌握整个配置体系的关键MiddlewareConfig是一个Map键是中间件的字符串标识如cors、express.json值是 Express 的RequestHandlerMiddlewareConfigFn是一个纯函数接收当前MiddlewareConfig通过Map.prototype.set/delete增删改后返回新的配置你自定义的函数会被 Wasp 在生成代码中调用产出的 Map 决定最终挂载哪些中间件。三个自定义层级的整体认知Wasp 提供三个位置来定制中间件作用范围从大到小全局global修改对所有 operationsquery 和 action以及所有api默认生效。例如为 CORS 增加多个允许域名。⚠️修改全局中间件需极度谨慎它会波及所有 operations 和 APIs。如果不确定请优先使用下面两种方式。api级per-api为单个 api 路由如POST /webhook/callback覆盖中间件。例如某个回调接口希望关闭 JSON 解析。路径级per-path为某个路径下的所有方法定制中间件。适合复杂 CORS 请求这类需要同时作用于OPTIONS和GET的场景也适合给一组api路由统一挂载中间件。1. 自定义全局中间件如果你希望对所有 operations 和 APIs修改中间件首先在main.wasp的app声明中通过server.middlewareConfigFn引用实现函数app todoApp { // ... server: { middlewareConfigFn: import { serverMiddlewareFn } from src/serverSetup }, }然后在src/serverSetup.js或.ts中实现该函数。下面的例子演示了为 CORS 追加多个额外域名在保留默认config.frontendUrl的基础上import cors from cors import { config } from wasp/server export const serverMiddlewareFn (middlewareConfig) { // 例如向 CORS 添加额外域名。 middlewareConfig.set(cors, cors({ origin: [config.frontendUrl, https://example1.com, https://example2.com] })) return middlewareConfig }TypeScript 版本import cors from cors import { config, type MiddlewareConfigFn } from wasp/server export const serverMiddlewareFn: MiddlewareConfigFn (middlewareConfig) { // 例如向 CORS 添加额外域名。 middlewareConfig.set(cors, cors({ origin: [config.frontendUrl, https://example1.com, https://example2.com] })) return middlewareConfig }这里用middlewareConfig.set(cors, ...)整体替换了默认的 CORS 中间件——注意默认配置中的 origin 是config.allowedCORSOrigins如果你想要既保留默认前端域名、又追加新域名更好的写法是基于config.allowedCORSOrigins展开这也是仓库多域名 CORS 指南采用的模式middlewareConfig.set(cors, cors({ origin: [...config.allowedCORSOrigins, https://example1.com, https://example2.com] }))底层全局配置的克隆保护机制从生成模板globalMiddleware.ts可以看出Wasp 生成的服务器代码是这样处理全局中间件的// 全局中间件将用户函数应用到默认配置的结果 const globalMiddlewareConfig: MiddlewareConfig serverMiddlewareFn(defaultGlobalMiddlewareConfig) // 供具体路由使用的入口函数 export function globalMiddlewareConfigForExpress(middlewareConfigFn?: MiddlewareConfigFn): express.RequestHandler[] { if (!middlewareConfigFn) { return Array.from(globalMiddlewareConfig.values()) } // 克隆一份避免任何路由污染全局 Map const globalMiddlewareConfigClone new Map(globalMiddlewareConfig) const modifiedMiddlewareConfig middlewareConfigFn(globalMiddlewareConfigClone) return Array.from(modifiedMiddlewareConfig.values()) }值得注意的实现细节globalMiddlewareConfigForExpress在应用某个路由特有的middlewareConfigFn前会先克隆全局 Map确保该路由的修改不会泄漏到其他路由或污染全局配置而你的全局函数serverMiddlewareFn则直接作用于默认配置之上其结果成为所有 operations/apis 的基座。2. 自定义api级中间件如果你只希望修改单个 api的中间件例如某个 webhook 回调在main.wasp中为该api声明middlewareConfigFn// ... api webhookCallback { fn: import { webhookCallback } from src/apis, middlewareConfigFn: import { webhookCallbackMiddlewareFn } from src/apis, httpRoute: (POST, /webhook/callback), auth: false }在src/apis.js或.ts中同时实现 api 处理器与中间件配置函数。下面的例子演示了用express.raw替换掉express.json以便接收原始请求体import express from express export const webhookCallback (req, res, _context) { res.json({ msg: req.body.length }) } export const webhookCallbackMiddlewareFn (middlewareConfig) { console.log(webhookCallbackMiddlewareFn: Swap express.json for express.raw) middlewareConfig.delete(express.json) middlewareConfig.set(express.raw, express.raw({ type: */* })) return middlewareConfig }TypeScript 版本import express from express import { type WebhookCallback } from wasp/server/api import { type MiddlewareConfigFn } from wasp/server export const webhookCallback: WebhookCallback (req, res, _context) { res.json({ msg: req.body.length }) } export const webhookCallbackMiddlewareFn: MiddlewareConfigFn (middlewareConfig) { console.log(webhookCallbackMiddlewareFn: Swap express.json for express.raw) middlewareConfig.delete(express.json) middlewareConfig.set(express.raw, express.raw({ type: */* })) return middlewareConfig }底层按方法per-method安装api级中间件是按 HTTP 方法粒度安装的。在生成的代码中最终效果等价于router.post(/webhook/callback, webhookCallbackMiddleware, ...)查看生成模板src/routes/apis/index.ts可以印证这一点每个 api 都会先通过globalMiddlewareConfigForExpress(routeMiddlewareConfigFn)生成自己的中间件数组再以router.{method}({path}, middleware, defineHandler(...))的形式注册到 Express Router 上const webhookCallbackMiddleware globalMiddlewareConfigForExpress(webhookCallbackMiddlewareFn) router.post( /webhook/callback, webhookCallbackMiddleware, defineHandler((req, res) { /* ... */ }) )若 api 开启了鉴权auth: true或全局开启模板还会把auth中间件放在自定义中间件之前形成[auth, ...webhookCallbackMiddleware]的链路。3. 自定义路径级per-path中间件如果你希望对某个公共路径下的所有 API 路由统一修改中间件可以为apiNamespace声明middlewareConfigFn// ... apiNamespace fooBar { middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from src/apis, path: /foo/bar }在src/apis.js或.ts中实现。下面的例子向该路径下的所有请求注入一个自定义日志中间件export const fooBarNamespaceMiddlewareFn (middlewareConfig) { const customMiddleware (_req, _res, next) { console.log(fooBarNamespaceMiddlewareFn: custom middleware) next() } middlewareConfig.set(custom.middleware, customMiddleware) return middlewareConfig }TypeScript 版本import express from express import { type MiddlewareConfigFn } from wasp/server export const fooBarNamespaceMiddlewareFn: MiddlewareConfigFn (middlewareConfig) { const customMiddleware: express.RequestHandler (_req, _res, next) { console.log(fooBarNamespaceMiddlewareFn: custom middleware) next() } middlewareConfig.set(custom.middleware, customMiddleware) return middlewareConfig }底层在路由器级别router level安装与api级按方法安装不同路径级中间件是在路由器级别对整个路径安装的。生成代码的最终效果等价于router.use(/foo/bar, fooBarNamespaceMiddleware)在生成模板src/routes/apis/index.ts中可以看到命名空间的处理逻辑模板先为每个apiNamespace生成中间件数组再通过router.use({namespacePath}, globalMiddlewareConfigForExpress(namespaceMiddlewareConfigFn))挂载之后该路径下的所有 api 路由都会先经过这层中间件。这也是处理复杂 CORS 请求需要同时覆盖OPTIONS预检和GET/POST实际请求或对一组 api 路由统一施加某中间件的标准方式。深入理解中间件在生成服务器中的整体装配为了对整体有清晰认知可以看根路由模板 src/routes/index.js 中全局中间件的装配顺序const router express.Router() const middleware globalMiddlewareConfigForExpress() // 全局中间件用户全局配置已生效 router.get(/, middleware, ...) // 根路径健康检查 router.use(/auth, middleware, auth) // 鉴权相关路由 router.use(/operations, middleware, operations) // operationsquery/action路由 // ... CRUD 路由同样挂载全局中间件 // 用户自定义 api 路由放在最后避免覆盖框架自带路由 // 这里不挂全局中间件以便后续支持 per-api / per-path 的定制。 router.use(apis)这段模板揭示了几条重要事实operations、auth、CRUD 路由共享同一份全局中间件因此修改全局配置会同时影响它们用户自定义api路由被刻意放在最后模板注释明确写着 Keep user-defined api routes last so they cannot override our routes且不自带中间件把定制权完全交给 per-api / per-path 的middlewareConfigFn在代码生成层面Haskell 生成器ApiRoutesG.hs负责把Api.middlewareConfigFn、ApiNamespace.middlewareConfigFn从 Wasp 声明转换为 TypeScript 导入语句并注入上述模板数据routeMiddlewareConfigFn.isDefined/importStatement/importAlias等最终产出可运行的src/routes/apis/index.ts。实战建议与注意事项优先使用更小的作用域能用 per-api / per-path 解决的就不要动全局中间件。全局修改会影响所有 operations 与 APIs误删express.json会导致 Operations 解析失败、误删 CORS 会导致前端无法访问后端。利用 Map 的键名语义helmet、cors、logger、express.json、express.urlencoded、cookieParser是内置键名覆盖时保持同名即可新增中间件建议使用带命名空间的键如custom.middleware避免与内置键冲突。修改而非重建全局示例中若需追加 CORS 域名优先展开config.allowedCORSOrigins再追加新域名更复杂的场景如按正则匹配子域、从环境变量读取域名列表可以参考仓库的多域名 CORS 配置指南。区分两种安装粒度api级按方法安装router.post(/webhook/callback, ...)路径级按路由器安装router.use(/foo/bar, ...)。路径级天然覆盖该路径下所有方法是复杂 CORS需同时处理OPTIONS预检与真实请求的推荐落点。配置的隔离性由框架保证globalMiddlewareConfigForExpress在应用路由级middlewareConfigFn前会克隆全局 Map单个路由的定制不会泄漏到其他路由——但全局函数本身直接作用于共享配置务必谨慎。掌握了这三个层级及其底层装配逻辑你就可以像操作普通 Express 应用一样精准控制 Wasp 生成的服务器中间件链全局加固、单接口换解析器、路径级统一拦截各取所需。【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。