ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Effect HttpApi 中间件错误 Schema 声明机制:从单错误到多错误数组的演进

Effect HttpApi 中间件错误 Schema 声明机制:从单错误到多错误数组的演进 Effect HttpApi 中间件错误 Schema 声明机制从单错误到多错误数组的演进【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code导读本文基于 Effect 生态中effect包的 unstable HttpApi 模块深入剖析一项类型系统与运行时行为并重的 API 变更允许 HttpApi 中间件HttpApiMiddleware通过数组形式声明多个错误 Schema。变更后中间件声明的错误在响应状态解析、客户端解码和生成的 API Schema 三个环节上与端点Endpoint错误的行为完全对齐。读完本文你将掌握 HttpApi 中间件错误声明的类型约束、运行时归一化实现以及如何在认证、鉴权、限流等横切关注点中声明多错误契约。变更背景一个 changeset 背后的类型契约升级本次变更记录于 .changeset/pre/calm-carrots-march.md--- effect: patch --- Allow unstable HttpApi middleware to declare multiple error schemas with arrays. Middleware errors now follow endpoint error behavior for response status resolution, client decoding, and generated API schemas.这是一次patch级别的变更不破坏既有 API而是放宽并统一中间件错误的表达能力。变更前中间件只能声明单个错误 Schema变更后可以像端点那样声明一组 Schema即错误联合由框架负责响应状态解析response status resolution根据实际发生的错误类型解析出对应的 HTTP 状态码客户端解码client decoding生成的客户端能够从响应体中按联合成员解码出正确的错误类型生成的 API SchemaOpenAPI 等派生 Schema 中正确呈现多个错误变体。类型层ErrorConstraint与ErrorSchemaFromConstraint中间件错误声明在类型层面由HttpApiMiddleware.ts中的两个关键类型驱动.repos/effect-smol/packages/effect/src/unstable/httpapi/HttpApiMiddleware.ts。错误约束单个 Schema 或 Schema 数组type ErrorConstraint Schema.Top | ReadonlyArraySchema.Top type ErrorSchemaFromConstraintE E extends ReadonlyArraySchema.Constraint ? E[number] : E extends Schema.Constraint ? E : neverErrorConstraint表示中间件错误既可以是单个 Schema也可以是只读 Schema 数组ErrorSchemaFromConstraint是提取逻辑当约束是数组时取数组元素类型E[number]构成联合当约束是单个 Schema 时直接使用该 Schema。这一条件类型正是多错误声明的类型层核心——数组中的每个成员都会被并入中间件失败的错误通道。贯穿三个模型服务端、安全中间件、类型标识ErrorConstraint被三个模型共用保证多错误声明在整个声明体系中一致生效// 普通服务端中间件失败通道为 unhandled | ErrorSchemaFromConstraintE[Type] export type HttpApiMiddlewareProvides, E extends ErrorConstraint, Requires ( httpEffect: Effect.EffectHttpServerResponse, unhandled, Provides, options: { readonly endpoint: HttpApiEndpoint.Top; readonly group: HttpApiGroup.Top } ) Effect.EffectHttpServerResponse, unhandled | ErrorSchemaFromConstraintE[Type], Requires | HttpRouter.Provided // 安全中间件每个 security scheme 的处理器拥有同样的错误约束 export type HttpApiMiddlewareSecurity... { readonly [K in keyof Security]: (...) Effect.Effect HttpServerResponse, unhandled | ErrorSchemaFromConstraintE[Type], Requires | HttpRouter.Provided } // 类型标识error 字段保存原始 ErrorConstraint含数组形态 export interface AnyId { readonly [TypeId]: { readonly provides: any readonly requires: any readonly error: ErrorConstraint readonly clientError: any readonly requiredForClient: boolean } }HttpApiMiddlewareSecurity的存在意味着带 security 的中间件如 Bearer 认证同样支持多个错误 Schema——每个 scheme 的凭据解码与错误声明共享同一约束。这与 changeset 中中间件错误遵循端点错误行为的承诺一致。提取工具类型错误联合与编解码服务由ErrorSchemaA出发派生出一整套面向错误联合的提取工具export type ErrorSchemaA A extends { readonly [TypeId]: { readonly error: infer E } } ? ErrorSchemaFromConstraintE : never export type ErrorA ErrorSchemaA[Type] export type ErrorServicesEncodeA ErrorSchemaA[EncodingServices] export type ErrorServicesDecodeA ErrorSchemaA[DecodingServices]ErrorA中间件失败时可能抛出的解码后错误类型联合ErrorServicesEncode/ErrorServicesDecode错误 Schema 各自携带的编码/解码服务需求会被自动并入中间件实现与生成的客户端所需环境中。运行时层Service构造器与getError归一化类型约束之外运行时实现同样处理了数组 vs 单个两种形态。Service构造器用于创建中间件服务类的签名与实现如下export const Service Self, Config extends { requires?: any; provides?: any; clientError?: any } ...(): const Id extends string, const Error extends ErrorConstraint never, // 错误约束默认 never const Security extends Recordstring, HttpApiSecurity.HttpApiSecurity never, RequiredForClient extends boolean false (id: Id, options?: { readonly error?: Error | undefined readonly security?: Security | undefined readonly requiredForClient?: RequiredForClient | undefined } | undefined) ServiceClass... (id: string, options?) { // ... self.error getError(options?.error) self.requiredForClient options?.requiredForClient ?? false if (options?.security ! undefined) { if (Object.keys(options.security).length 0) { throw new Error(HttpApiMiddleware.Service: security object must not be empty) } // ... } return self }getError把任意输入归一化为只读错误集合function getError(error: ErrorConstraint | undefined): ReadonlySetSchema.Top { if (error undefined) return new Set() return new Set(Array.isArray(error) ? error : [error]) }这是本次变更在运行时的落点error未声明 → 空集合不产生错误契约声明单个 Schema → 包装为单元素集合声明 Schema 数组 → 展开为多元素集合。ServiceClass的静态侧通过readonly error: ReadonlySetSchema.Top暴露该集合供端点到中间件的校验、客户端生成与 OpenAPI 派生逻辑消费。AnyService接口同样以ReadonlySetSchema.Top保存错误集合确保所有消费方看到统一的运行时视图。错误如何被实际应用HttpApiBuilder的中间件流水线中间件声明只是契约真正被执行是在HttpApiBuilder构建路由时。在 .repos/effect-smol/packages/effect/src/unstable/httpapi/HttpApiBuilder.ts 中applyMiddleware遍历端点挂载的中间件并按序套用const applyMiddleware Group extends HttpApiGroup.Constraint, A extends Effect.Effectany, any, any( // ... ) { // ... for (const key_ of endpoint.middlewares) { const key key_ as HttpApiMiddleware.AnyService const apply HttpApiMiddleware.isSecurity(key) ? makeSecurityMiddleware(key, service) // 安全中间件走专用路径 : // 普通中间件套用 httpEffect ... 的包装逻辑 } }普通中间件把端点响应 Effect 包装为httpEffect ...形式失败的错误通道中并入中间件声明的ErrorSchemaFromConstraintE[Type]安全中间件通过makeSecurityMiddleware为每个 security scheme 的凭据解码分配独立的处理器securityMiddlewareCacheWeakMap避免重复构建。因此当中间件以数组声明多个错误 Schema 时端点执行链路中的失败要么来自端点自身、要么来自中间件联合中的任意成员——二者在HttpApiBuilder的错误通道中被统一处理这正是 changeset 所述遵循端点错误行为的架构含义。实战示例用数组声明多个错误 Schema结合 layerSchemaErrorTransform把HttpApiSchemaError转换为自定义错误的层演示多错误声明import { Effect, Schema } from effect import { HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiMiddleware } from effect/unstable/httpapi // 1. 定义两个不同的错误变体 class RateLimited extends Schema.TaggedErrorRateLimited()(RateLimited, { retryAfterSeconds: Schema.Number }) {} class Unauthorized extends Schema.TaggedErrorUnauthorized()(Unauthorized, {}) {} // 2. 以“数组”形式声明中间件可产生的多个错误 Schema class AuthGuard extends HttpApiMiddleware.ServiceAuthGuard()(api/AuthGuard, { error: [Unauthorized, RateLimited] // 数组 → 错误联合 }) {} // 3. 将端点产生的 schema 错误转换为中间件声明的错误 const AuthGuardLayer HttpApiMiddleware.layerSchemaErrorTransform( AuthGuard, (schemaError) Effect.fail(schemaError.kind RateLimited ? new RateLimited({ retryAfterSeconds: 30 }) : new Unauthorized()) ) // 4. 端点与组正常声明 const endpoint HttpApiEndpoint.get(example, /).addError(RateLimited) const group HttpApiGroup.make(examples).add(endpoint)要点说明error选项的类型为ErrorConstraint即Schema.Top | ReadonlyArraySchema.Top数组形式与单个形式均合法声明后AuthGuard的静态error集合包含两个成员ErrorAuthGuard的类型为Unauthorized | RateLimited生成的客户端将按端点错误同样的机制从响应中解码出对应错误变体生成的 API Schema 也会同时呈现两个错误响应。测试与类型验证覆盖多错误声明的能力已进入仓库的测试与类型测试矩阵.repos/effect-smol/packages/effect/typetest/unstable/httpapi/HttpApiMiddleware.tst.ts针对中间件错误约束、客户端错误、ForClient标记做编译期断言.repos/effect-smol/packages/effect/typetest/unstable/httpapi/HttpApiBuilder.tst.ts验证中间件声明在组/端点构建中的类型收敛.repos/effect-smol/packages/effect/test/unstable/httpapi/HttpApiBuilder.test.ts 与 .repos/effect-smol/packages/platform/node/test/HttpApi.test.ts覆盖中间件套用、安全凭据处理与运行时错误路径.repos/effect-smol/packages/effect/test/unstable/httpapi/OpenApi.test.ts 与 .repos/effect-smol/packages/platform/node/test/OpenApi.test.ts验证错误联合正确投影到生成的 OpenAPI Schema。若需在既有代码中启用该能力注意HttpApi 模块目前位于unstable命名空间导入路径为effect/unstable/httpapiHttpApiMiddleware.Service要求since 4.0.0即effect4.x 及以上版本并确认该 changeset 已随目标版本发布。总结本次 changeset 揭示的是一次小改动、大统一的类型系统演进声明能力HttpApiMiddleware.Service的error选项从单个 Schema 扩展为Schema.Top | ReadonlyArraySchema.Top数组中的每个成员都成为中间件错误联合的组成部分类型推导ErrorSchemaFromConstraint在类型层将数组约束展开为联合ErrorA、ErrorServicesEncode/DecodeA随之生效运行时归一化getError将数组/单个/未声明统一收敛为ReadonlySetSchema.Top供服务类静态元数据使用行为对齐中间件错误在响应状态解析、客户端解码、生成 API Schema 三个环节与端点错误完全一致并由HttpApiBuilder.applyMiddleware统一并入错误通道。对于使用 Effect 构建 schema 驱动 HTTP API 的团队这意味着认证、限流、授权等横切中间件现在可以像端点一样表达完整的错误契约无需再依赖单个错误的妥协方案。【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表