Strapi OpenAPI 生成器处理器扩展机制:Pre-Processor 与 Post-Processor 的实现与注册详解
2026/9/7 2:05:49 网站建设 项目流程

Strapi OpenAPI 生成器处理器扩展机制:Pre-Processor 与 Post-Processor 的实现与注册详解

【免费下载链接】strapi🚀 Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable, and developer-first.项目地址: https://gitcode.com/GitHub_Trending/st/strapi

本文围绕 Strapi 核心包@strapi/openapi的生成生命周期,讲解如何在文档组装前后挂载 pre-processor 与 post-processor。读完你可以掌握两类处理器的接口契约、注册工厂(PreProcessorFactory/PostProcessorsFactory)的接线位置,以及现有实现ComponentsWriter如何把 Zod 内容 API 注册表写成components.schemas,从而为 OpenAPI 文档生成管线贡献自己的横切处理逻辑。

生成器执行顺序:处理器在整个生命周期中的位置

OpenAPI 生成器按固定顺序执行三类组件,这一顺序在 OpenAPIGenerator.generate 中以链式调用的形式实现:

this // Init timers ._bootstrap(context) // Run registered pre-processors ._preProcess(context) // Run registered section assemblers ._assemble(context) // Run registered post-processors ._postProcess(context) // Clean up and set necessary properties ._finalize(context);

即:

  1. Pre-processors(预处理)—— 在文档组装前准备DocumentContext
  2. Assemblers(组装器)—— 构建 OpenAPI 文档的各个部分;
  3. Post-processors(后处理)—— 在组装完成后对文档做最终处理。

三类组件都以for...of循环按注册顺序逐个执行(见 _preProcess 与 _postProcess),每一步都带有debug('running pre-processor: %s...')之类的调试日志,开启 debug 模式时可以看到每个处理器类的执行轨迹。

两类处理器接收的都是完整的DocumentContext。从 类型定义 看,DocumentContextData就是Partial<OpenAPIV3_1.Document>,因此context.output.data是一份可增量修改的 OpenAPI 3.1 文档骨架。DocumentContext本身由 context/types.ts 中的泛型Context<T>定义,包含五个字段:

字段类型说明
routesCore.Route[]经路由收集器筛选后的 Strapi 路由列表
strapiCore.StrapiStrapi 应用实例,可访问 schema 注册表等运行时能力
timerTimer生成计时器,用于统计耗时
registriesContextRegistriesRegistriesFactory创建的一组注册表,包括存放抽取组件 schema 的extractedComponentSchemas
output{ data, stats }data为正在构建的文档,stats.time记录startTime / endTime / elapsedTime

处理器实例在 src/exports.ts 导出的generate()入口中统一装配:

const config = { preProcessors: new PreProcessorFactory().createAll(), assemblers: new DocumentAssemblerFactory().createAll(), postProcessors: new PostProcessorsFactory().createAll(), };

生成的config连同strapi实例、RouteCollectorDocumentContextFactory一起注入OpenAPIGenerator构造函数。因此新增处理器只需修改对应工厂的createAll()返回值,无需触碰生成器本体。

添加一个 Post-Processor

当前代码库中唯一内置的后处理器是ComponentsWriter:它在组装结束后从strapi.contentAPISchemaRegistry(一个 Zod 注册表)写出components.schemas;而在路由转换阶段通过嵌套 Zod.meta({ id })收割到的 schema,则通过context.registries.extractedComponentSchemas一并合并,保证文档中的$ref都能解析到真实定义。

1. 创建处理器类

PostProcessor 接口 只有一个方法:

import type { DocumentContext } from '../types'; import type { PostProcessor } from './types'; export class ExamplePostProcessor implements PostProcessor { postProcess(context: DocumentContext): void { // mutate context.output.data } }

接口签名为postProcess(context: DocumentContext): void,约定处理器直接就地修改context.output.data(即文档对象本身),而不是返回新文档。

2. 在 PostProcessorsFactory 中注册

在 post-processor/factory.ts 中追加实例即可。当前实现为:

export class PostProcessorsFactory { createAll(): PostProcessor[] { return [new ComponentsWriter()]; } }

注册新的处理器后形如:

import { ComponentsWriter } from './component-writer'; import { ExamplePostProcessor } from './example'; export class PostProcessorsFactory { createAll(): PostProcessor[] { return [new ComponentsWriter(), new ExamplePostProcessor()]; } }

数组顺序即执行顺序:后处理器按注册先后依次运行,因此依赖前一个处理器产出的处理器要排在后面。

深入实现:ComponentsWriter 做了什么

ComponentsWriter.postProcess 的完整流程可作为编写后处理器的参照样板:

  1. context.strapi.contentAPISchemaRegistry.entries()读取内容 API 的 Zod schema,逐个以{ id }元数据加入一个本地z.registry
  2. 调用z.toJSONSchema(registry, { ...OPENAPI_SCHEMA_CONVERSION_OPTIONS, uri: toComponentsPath })把 Zod 定义转换为 OpenAPI schema,uri选项(toComponentsPath)决定$ref的目标路径;
  3. liftZodSharedDefinitions(converted)提升 Zod 转换产生的共享定义;
  4. 将结果与output.data.components中已有内容做浅合并,同时把context.registries.extractedComponentSchemas(路由转换阶段收割的嵌套.meta({ id })schema)放在前面展开,再叠加注册表转换出的 schema,确保引用与定义一致。

值得注意的是它对空输出的防御:isPlainObject(schemas) ? schemas : {},保证注册表为空时不会写出undefined

Pre-Processor 的工作方式

Pre-processor 与 post-processor 完全对称,接口定义在 pre-processor/types.ts:

export interface PreProcessor { preProcess(context: DocumentContext): void; }

注册方式相同:在 PreProcessorFactory 的createAll()中返回实例。当前该工厂返回空数组(return [];),说明仓库中尚无内置的 pre-processor——这是一个明确的扩展位点,适合放置“在组装开始前清理/注入上下文、预热注册表、标注路由元数据”一类的工作。

选型原则:优先用 Assembler,处理器只用于横切逻辑

:::tip 官方贡献指南的建议

构建文档的各个 section 应优先使用 assembler;processor 只用于必须在完整组装通过前后执行的横切(cross-cutting)工作。

:::

从 assemblers/types.ts 可以看到,assembler 家族按粒度分层:Assembler.Document拿到DocumentContextAssembler.Path/PathItem/Operation分别拿到更窄的上下文。文档级 section(如infoserverssecurity)的构建都落在Assembler.Document层,而操作级细节(operationId、参数、响应、tag)由更细的上下文组装器负责。只有当你的逻辑依赖整份文档的终态(典型如ComponentsWriter需要等所有$ref都写入后才汇总 schema),或者需要在组装前改变上下文时,才应使用 pre/post-processor。

小结与关键文件索引

  • 生成管线与执行顺序:generator.ts(_preProcess → _assemble → _postProcess
  • 工厂接线位置:exports.ts(PreProcessorFactory/PostProcessorsFactory在此实例化)
  • Post-processor 接口与工厂:types.ts、factory.ts
  • 现成后处理器实现:component-writer.ts
  • Pre-processor 接口与工厂:types.ts、factory.ts(当前为空注册表)
  • DocumentContext结构:types.ts、context/types.ts
  • Assembler 分层接口(选型对比):assemblers/types.ts
  • 本文档来源:05-processors.md

需要说明的前提:@strapi/openapigenerate()入口在源码注释中标记为@experimental,上述处理器机制基于当前仓库快照的实际代码,接口签名(如postProcess(context): void的 void 返回值约定)可能随后续版本演进。

【免费下载链接】strapi🚀 Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable, and developer-first.项目地址: https://gitcode.com/GitHub_Trending/st/strapi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询