Skip to content

08 · 代码生成:一份 yaml → 前端 SDK + 后端骨架 + Mock 服务

生活类比:你是开餐厅老板,写了一份完整菜单(openapi.yaml)。 代码生成器 = 印刷厂——一份稿子,一键给你印出:

  • 前厅用的"点单 PAD 软件"(前端 SDK)
  • 后厨贴的"出餐流程图"(后端 Controller 骨架)
  • 临时给客人看的"试吃版"(Mock 服务)

没有印刷厂,你得手抄 50 份——这就是没有 codegen 的痛。


1. 为什么要"代码生成"

       openapi.yaml

            ├─►  前端 TypeScript SDK         ← 类型安全 + IDE 自动补全
            ├─►  Java SDK / Go SDK / Swift...
            ├─►  后端 Spring/Express Controller 骨架
            ├─►  Mock 服务(响应符合 schema)
            ├─►  Postman 集合
            ├─►  Markdown / Asciidoc 文档
            └─►  TypeScript 类型定义文件 (.d.ts)

好处

  • ✅ 接口改字段,重新生成,前端 IDE 立刻报红
  • ✅ 后端不用手写 DTO、Controller 入参签名
  • ✅ 前端联调时直接 new UsersApi().listUsers(),不用拼 URL
  • ✅ Mock 服务让前端不必等后端

2. 三大主流代码生成器

2.1 openapi-generator(推荐,社区主流)

GitHub 60k+ star,社区维护,支持 50+ 语言/框架

bash
# Docker 一句话(推荐)
docker run --rm -v $PWD:/local \
  openapitools/openapi-generator-cli generate \
  -i /local/openapi.yaml \
  -g typescript-axios \
  -o /local/sdk-ts

# npm 全局
npm i -g @openapitools/openapi-generator-cli
openapi-generator-cli generate -i openapi.yaml -g typescript-axios -o ./sdk-ts

# 列出所有支持的 generator
openapi-generator-cli list

常用 -g 选项

-g生成什么
typescript-axiosTS 客户端,axios 实现(最常用
typescript-fetchTS 客户端,原生 fetch 实现
typescript-angularAngular HttpClient
typescript-rxjsRxJS Observable 风格
javascript纯 JS
javaJava SDK
goGo SDK
pythonPython SDK
swift5iOS
kotlinAndroid / Kotlin Multiplatform
springSpring Boot Controller 骨架
nodejs-express-serverExpress 骨架
aspnetcoreASP.NET Core
html2静态 HTML 文档

2.2 swagger-codegen(老牌,Swagger 官方)

跟 openapi-generator 是同源的两个分支(fork 自 swagger-codegen v2)。 openapi-generator 更新快、社区活跃,新项目无脑选它。

2.3 swagger-typescript-api(轻量级前端专用)

bash
npm i -g swagger-typescript-api
swagger-typescript-api -p ./openapi.yaml -o ./src/api -n api.ts

特点:只生成一个 .ts 文件,零依赖(除了你选的 fetch/axios),适合"我不想要个 SDK 大目录"的场景。


3. 实战 · 生成前端 TypeScript SDK

3.1 准备一份 yaml

yaml
# openapi.yaml
openapi: 3.0.3
info: { title: User API, version: 1.0.0 }
servers:
  - url: http://localhost:3000

paths:
  /users:
    get:
      operationId: listUsers
      tags: [user]
      parameters:
        - name: page
          in: query
          schema: { type: integer, default: 1 }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: array
                items: { $ref: '#/components/schemas/User' }
    post:
      operationId: createUser
      tags: [user]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/UserCreate' }
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema: { $ref: '#/components/schemas/User' }

  /users/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
    get:
      operationId: getUser
      tags: [user]
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/User' }

components:
  schemas:
    User:
      type: object
      properties:
        id: { type: integer, readOnly: true }
        name: { type: string }
        email: { type: string, format: email }
      required: [id, name, email]
    UserCreate:
      type: object
      properties:
        name: { type: string }
        email: { type: string, format: email }
      required: [name, email]

3.2 生成 SDK

bash
docker run --rm -v $PWD:/local \
  openapitools/openapi-generator-cli generate \
  -i /local/openapi.yaml \
  -g typescript-axios \
  -o /local/sdk

3.3 生成的目录长这样

sdk/
├── api.ts                ← 主入口,所有 API 类
├── base.ts
├── common.ts
├── configuration.ts
├── index.ts
└── models/
    ├── user.ts           ← User 类型
    └── user-create.ts    ← UserCreate 类型

3.4 业务代码这样调

typescript
import { Configuration, UserApi, UserCreate } from './sdk';

const config = new Configuration({
  basePath: 'http://localhost:3000',
  accessToken: () => localStorage.getItem('token') || '',
});
const api = new UserApi(config);

// IDE 自动补全 + 类型检查!
const { data: users } = await api.listUsers(1);    // 返回 User[]

const newUser: UserCreate = { name: 'Alice', email: 'alice@example.com' };
const { data: created } = await api.createUser(newUser);

console.log(created.id);          // ✅ 编译期就知道有 id 字段
// console.log(created.foo);      // ❌ 编译报错:Property 'foo' does not exist

关键收益:接口改了字段(如把 name 改成 nickname),重新生成 → 所有用 user.name 的地方 IDE 立刻飘红——再也不会发版后才发现。


4. 实战 · 生成后端 Spring Boot 骨架

bash
docker run --rm -v $PWD:/local \
  openapitools/openapi-generator-cli generate \
  -i /local/openapi.yaml \
  -g spring \
  -o /local/server-spring \
  --additional-properties=interfaceOnly=true,useSpringBoot3=true

生成的 UsersApi.java(接口,不含实现):

java
public interface UsersApi {

    @Operation(summary = "", operationId = "listUsers", responses = { ... })
    @RequestMapping(method = RequestMethod.GET, value = "/users", produces = "application/json")
    ResponseEntity<List<User>> listUsers(
        @Parameter @RequestParam(value = "page", required = false, defaultValue = "1") Integer page
    );

    @RequestMapping(method = RequestMethod.POST, value = "/users", consumes = "application/json")
    ResponseEntity<User> createUser(@Valid @RequestBody UserCreate userCreate);

    @RequestMapping(method = RequestMethod.GET, value = "/users/{id}")
    ResponseEntity<User> getUser(@PathVariable("id") Integer id);
}

你只要写一个 @RestController implements UsersApi,专注业务逻辑——所有签名、参数校验、文档注解都已经生成好


5. 实战 · Mock 服务(Prism)

前端不想等后端写完?用 openapi.yaml + Prism,马上起一个能返回符合 schema 的假数据的服务。

bash
docker run --rm -p 4010:4010 \
  -v $PWD:/spec \
  stoplight/prism:4 mock -h 0.0.0.0 /spec/openapi.yaml

请求:

bash
curl http://localhost:4010/users

返回(按 schema 自动生成):

json
[
  { "id": 0, "name": "string", "email": "user@example.com" }
]

如果你在 yaml 里给了 example,Prism 会优先用:

yaml
schema:
  $ref: '#/components/schemas/User'
example:
  id: 1
  name: Alice
  email: alice@example.com

Mock 服务连接 Swagger UI 的 Try it out,前端可以完全独立调试


6. 配置文件 · openapi-generator.yaml

不想每次命令行写一长串:

yaml
# openapi-generator.yaml
generatorName: typescript-axios
inputSpec: ./openapi.yaml
outputDir: ./sdk
additionalProperties:
  npmName: '@company/api-sdk'
  npmVersion: '1.0.0'
  withInterfaces: true
  supportsES6: true
  enumPropertyNaming: original
bash
openapi-generator-cli generate -c openapi-generator.yaml

6.1 常用 additionalProperties(typescript-axios)

选项作用
npmName生成 package.json 的包名
npmVersion包版本
supportsES6输出 ES6 语法
withInterfaces把 API 类拆成接口
withSeparateModelsAndApi模型 / API 分目录
useSingleRequestParameter多参数合成一个对象(更易读)
enumPropertyNamingenum 命名风格(original / camelCase)

7. 整合到 CI / 项目脚本

7.1 npm scripts

json
{
  "scripts": {
    "gen": "openapi-generator-cli generate -i openapi.yaml -g typescript-axios -o src/api",
    "gen:server": "openapi-generator-cli generate -i openapi.yaml -g spring -o ../backend/src/main/java",
    "lint:api": "spectral lint openapi.yaml",
    "mock": "prism mock openapi.yaml"
  }
}

7.2 GitHub Action / GitLab CI

yaml
# .github/workflows/api.yml
name: API codegen
on:
  push:
    paths: ['openapi.yaml']

jobs:
  generate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Lint
        run: npx @stoplight/spectral-cli lint openapi.yaml
      - name: Generate TS SDK
        run: |
          npx @openapitools/openapi-generator-cli generate \
            -i openapi.yaml -g typescript-axios -o sdk-ts
      - name: Publish to npm
        run: cd sdk-ts && npm publish --access public
        env: { NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} }

效果:后端改 yaml → push → 自动跑 lint → 自动生成新 SDK → 自动发到 npm → 前端 npm update 就拿到最新类型。


8. 不只是代码:从 yaml 派生其他物料

工具输入输出
openapi-generatoryaml50+ 语言 SDK + 后端骨架
PrismyamlMock 服务
Postman (导入)yamlPostman 集合
@redocly/cli build-docsyaml单页 HTML 文档
spectralyamlLint / 质量报告
schemathesis (Python)yaml自动模糊测试
openapi-diffyaml1 + yaml2接口变更报告(breaking 检测)
openapi-typescriptyaml.d.ts 类型文件

9. 工程最佳实践

✅ 把 openapi.yaml 视作"源代码",跟业务代码一起 Git
✅ CI 里跑 spectral lint(确保 yaml 质量)
✅ CI 里跑 openapi-diff(检测 breaking change)
✅ 后端用 generator 出 interface,业务实现 implements 它
✅ 前端用 generator 出 SDK,私有 npm 发布或 git submodule
✅ 每次发版打 tag,让前后端版本可追溯
❌ 别把生成的代码也提交 Git(写进 .gitignore)——CI 时再生成
❌ 别手动改生成的代码——每次 generate 都被覆盖

10. 章末面试题速览

  1. 为什么要代码生成?直接用 axios 不行吗? → 直接 axios 调用:URL 字符串拼出来 / 入参出参没类型 / 接口改字段不会报错。生成 SDK:类型安全 + IDE 补全 + 接口变更全链路 IDE 报红。
  2. swagger-codegen 和 openapi-generator 选哪个? → 新项目无脑 openapi-generator,社区维护、更新快、模板多。
  3. 接口变更怎么及时通知前端? → CI 里跑 openapi-diff 检测 breaking → 自动评论 PR;CI 自动重新生成 SDK 并发布 npm。

🎬 可视化演示

下方 demo 模拟"一份 yaml 同时生成 4 种产物"的全过程,让你直观看到 codegen 的爆发力。

→ 打开 08_codegen/demo.html

🎬 可视化演示

演示加载缓慢或样式异常?点此在新标签页打开 ↗