主题
Swagger / OpenAPI · 高频面试题(30+)
涵盖前端、后端、测试 / QA 三类岗位真题。难度从 ⭐ 入门到 ⭐⭐⭐⭐⭐ 专家。每题都给"简短答案 + 展开"两种深度,按场合(电话面 / 现场白板)选用。
🌱 入门题(⭐ — ⭐⭐)
1. Swagger 和 OpenAPI 是同一个东西吗?
简短答:不是。OpenAPI 是规范,Swagger 是工具。
展开:
- 2010 年:Tony Tam 在 Wordnik 公司开发 Swagger(包含规范 + UI + Codegen 一整套)
- 2015 年:SmartBear 收购 Swagger,把"规范"捐给 Linux 基金会,更名为 OpenAPI Specification (OAS)
- 从此:OpenAPI = 规范(YAML/JSON 怎么写);Swagger = 工具家族(Swagger UI / Editor / Codegen)
- 日常口语两者经常混用,但面试时讲清这层关系会加分
2. 为什么要用 Swagger,不用 Word 文档 / 语雀?
简短答:Word 文档 = "手写菜单",会过期;Swagger = "机器可读的菜单",能自动生成 SDK / Mock / 测试集合,且永远跟代码同步。
展开:
- 准确性:Word 容易过期;OpenAPI 是 code-first 自动导出的,不会撒谎
- 可运行:Swagger UI 自带
Try it out,所见即所得 - 自动化:一份 yaml → 50+ 语言 SDK + 后端骨架 + Mock + Postman 集合
- 跨语言协作:所有人看同一份"事实源",不用群里追后端问字段
3. OpenAPI 的根字段必填的有哪几个?
简短答:3 个 — openapi、info(含 title + version)、paths。
展开:
yaml
openapi: 3.0.3 # 必填
info: # 必填,里面 title 和 version 必填
title: My API
version: 1.0.0
paths: {} # 必填,可以是空对象servers / components / tags / security / externalDocs 都是可选。
4. openapi.yaml 和 openapi.json 区别?
简短答:等价。两种格式工具都支持,人手写选 yaml,机器输出多是 json。
展开:
- yaml 更紧凑(小 30%)、支持注释、缩进易读但易出错
- json 工具兼容性最好,但啰嗦、不能写注释
- 后端框架(Spring / FastAPI)自动暴露的多是 json
- 可以用
yq、js-yaml互转
5. Swagger UI 和 Swagger Editor 区别?
简短答:UI 是"展示器"(只读),Editor 是"编辑器"(可改可预览)。
展开:
- Swagger UI:渲染一份现成的 yaml 成漂亮的交互式文档(带 Try it out)
- Swagger Editor:左边写 yaml,右边实时渲染(适合学习 / 手写)
- 都能 Docker 一句话起;Editor 多用于本地开发,UI 多用于团队 / 客户
6. Swagger UI 和 Redoc 怎么选?
简短答:内部团队 / 调试用 Swagger UI(带 Try it out);外部展示 / 文档站用 Redoc(更漂亮、长文档体验更好)。
展开:
| 维度 | Swagger UI | Redoc |
|---|---|---|
| Try it out | ✅ 支持 | ❌ 默认不支持 |
| 美观度 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 长文档 | 一般 | 强(左侧导航 + 三栏) |
| 自定义 | 较弱 | 强 |
7. OpenAPI 2.0 和 3.0 有啥区别?
简短答:3.0 重构了结构——servers 替代 host/basePath、requestBody 独立、components 集中复用、对 OAuth2 / JSON Schema 友好。
展开:
| 维度 | OpenAPI 2.0 (Swagger 2) | OpenAPI 3.0 / 3.1 |
|---|---|---|
| 顶层字段 | swagger: "2.0" | openapi: 3.0.3 |
| 服务器地址 | host + basePath + schemes | servers 数组 |
| 请求体 | 混在 parameters 里 | 独立 requestBody |
| 复用区 | definitions 等平铺 | 统一进 components |
| 多 MIME | consumes / produces | 每个 contentType 独立 |
| OAuth2 | 不直观 | 重写、字段清晰 |
| JSON Schema | 部分兼容 | 3.1 完全兼容 (2020-12) |
8. yaml 里 200 状态码为啥要加引号?
简短答:yaml 把裸的 200 解析成数字;OpenAPI 规定状态码必须是字符串。
展开:
yaml
# ✗ 错(200 被当数字)
responses:
200:
description: OK
# ✓ 对
responses:
'200':
description: OK类似的:'true' / 'false' / 'on' / 'off' 在 yaml 里别裸写,否则被当 boolean。
🌿 进阶题(⭐⭐⭐)
9. $ref 是什么?常见错误?
简短答:$ref 是 OpenAPI 的引用机制,让你复用 schema / parameters / responses / examples。语法是 JSON Pointer。
展开:
yaml
# 当前文件内(最常见)
$ref: '#/components/schemas/User'
# 其他文件
$ref: './schemas/user.yaml#/User'常见坑:
yaml
# ✗ 漏 #
$ref: 'components/schemas/User'
# ✗ 拼写
$ref: '#/components/schema/User' # schemas 漏 s
# ✗ $ref 不能跟其他字段并列
schema:
$ref: '#/components/schemas/User'
description: '用户' # 会被忽略10. allOf / oneOf / anyOf 的区别?
简短答:
allOf= 全部都满足(合并 / 继承)oneOf= 恰好满足其中一个(多态)anyOf= 至少满足一个(可同时满足多个)
展开:
yaml
# allOf:Dog = Pet + 额外字段
Dog:
allOf:
- $ref: '#/components/schemas/Pet'
- type: object
properties: { breed: { type: string } }
# oneOf:付款方式只能选一种
PaymentMethod:
oneOf:
- $ref: '#/components/schemas/CreditCard'
- $ref: '#/components/schemas/Alipay'
discriminator:
propertyName: type
# anyOf:支持多种但能复合
Search:
anyOf:
- type: object
properties: { keyword: { type: string } }
- type: object
properties: { author: { type: string } }工程里 90% 用 allOf(继承 + 扩展),oneOf 多用于多态请求体。
11. PUT 和 PATCH 的区别?
简短答:
- PUT = 完整替换:传整个对象,没传的字段会被清空
- PATCH = 部分更新:只改你传的字段,其他保留
展开:
http
PUT /users/1 PATCH /users/1
{ "name": "Alice", "email": "" } { "name": "Alice" }
→ email 被清空 → email 保持原样幂等性:PUT 永远幂等;PATCH 看实现({op: "+", balance: 100} 不幂等)。
12. POST 和 PUT 都能创建资源,怎么选?
简短答:URL 里不知道 ID 时用 POST(服务端生成 ID);知道 ID 时用 PUT(客户端指定 ID)。
展开:
POST /users → 服务端返回 201 + Location: /users/999(ID=999 是服务端生成)
PUT /users/u-abc → 客户端指定 ID 创建(如导入已有数据)PUT 还要求幂等:连续调 N 次效果跟 1 次一样。POST 不要求幂等。
13. operationId 写不写有差别吗?
简短答:强烈推荐写。codegen 用它当生成的方法名,写了名字漂亮(createUser()),不写就是丑的(usersPost())。
展开:
yaml
post:
operationId: createUser # ← 推荐→ 生成的 SDK:api.createUser({...}) → 不写则可能:api.usersPost({...}) 或 api.users_create({...})
约定:驼峰 + 动词在前。在整份 yaml 里 operationId 必须唯一。
14. path 参数为什么必须 required: true?
简短答:URL 里的 {} 没法"省略",所以一定要传。OpenAPI 规范强制要求。
展开:
yaml
- name: id
in: path
required: true # ← path 永远 true
schema: { type: integer }写 false 工具会报错。其他位置(query / header / cookie)默认 false。
15. query 数组在 URL 里怎么表示?
简短答:通过 style + explode 控制:
| style | explode | URL |
|---|---|---|
| form | true | ?tags=a&tags=b (默认) |
| form | false | ?tags=a,b |
| spaceDelimited | false | ?tags=a%20b |
| pipeDelimited | false | ?tags=a|b |
展开:实际工程保持默认就好(form + explode=true),最兼容。
16. 文件上传怎么写?
简短答:用 multipart/form-data + format: binary。
展开:
yaml
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary # ← 关键
description:
type: string
required: [file]多文件:
file: { type: array, items: { type: string, format: binary } }
17. readOnly / writeOnly 啥用?
简短答:readOnly 字段只在响应里出现,请求体不传(如 id / createdAt);writeOnly 反过来(如 password)。
展开:
yaml
User:
type: object
properties:
id: { type: integer, readOnly: true }
password: { type: string, writeOnly: true }
email: { type: string }代码生成时:
- 创建 / 更新的 DTO 类型自动剔除 readOnly 字段
- 响应类型自动剔除 writeOnly 字段
- 类型更安全,前端不会误传 id
18. Authorization 头要写在 parameters 还是 securitySchemes?
简短答:securitySchemes!parameters 不允许描述安全相关的头。
展开:
yaml
# ✗ 错
parameters:
- name: Authorization
in: header
schema: { type: string }
# ✓ 对
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- bearerAuth: []写到 parameters 不会报错,但 Swagger UI 不会出现 🔒 Authorize 按钮,不规范。
19. 前端 Try it out 跨域怎么办?
简短答:让后端开 CORS(Access-Control-Allow-Origin);或前面挂 Nginx 反向代理同源。
展开:
Swagger UI 的 Try it out 是浏览器从你电脑发的真实请求,跨域时浏览器会拒绝。
# 后端临时方案:Spring Boot
@CrossOrigin(origins = "*")
@RestController public class XxxController { ... }
# Nginx 反向代理把 /api 代理到后端,从而同源🌳 高级题(⭐⭐⭐⭐)
20. JWT 的 payload 是加密的吗?
简短答:不是!只是 base64 编码,谁拿到 token 都能解出来。
展开:
header.payload.signature
↑ ↑ ↑
| | └─ 服务端用密钥签名,防篡改
| └────────── base64({"sub": "123", "exp": ..., "role": "admin"})
└────────────────── base64({"alg": "HS256"})把整段 token 贴到 https://jwt.io 立刻能看 payload 全部内容。所以:
- ❌ 别在 payload 里塞密码、隐私信息
- ✅ 只放 user_id、role、过期时间这种"非敏感的元信息"
- 安全靠的是 signature:篡改 payload 后服务端算签名对不上,就拒绝
21. OAuth2 的 4 种 flow 选哪个?
简短答:
| Flow | 用谁 | 现状 |
|---|---|---|
authorizationCode | Web 应用 + 后端 | ✅ 推荐 |
authorizationCode + PKCE | SPA / 移动端 | ✅ SPA 首选 |
clientCredentials | M2M (服务到服务) | ✅ 后端通信 |
password | 第一方应用 | ⚠️ 不推荐 |
implicit | 老 SPA | ❌ 已废弃 |
展开:implicit 因为 token 走 URL fragment 易泄漏(浏览器历史 / 日志)已经被 OAuth2.1 废弃,新 SPA 用 authorizationCode + PKCE。
22. swagger-codegen 和 openapi-generator 选哪个?
简短答:openapi-generator。
展开:
- swagger-codegen:Swagger 官方早期工具,更新慢
- openapi-generator:2018 年从 swagger-codegen v2 fork,社区维护、活跃度高、模板多
- 新项目无脑选 openapi-generator
23. springfox 和 springdoc-openapi 选哪个?
简短答:springdoc-openapi。springfox 已停止维护,且只支持 OpenAPI 2.0。
展开:
- springfox:老项目用得多,最后版本 3.0 仍然只是 OpenAPI 2.0
- springdoc-openapi:原生支持 OpenAPI 3,活跃维护,跟 Spring Boot 3 兼容好
- 迁移命令大概率把
@Api→@Tag、@ApiModelProperty→@Schema
24. @nestjs/swagger 的 CLI 插件是干啥的?
简短答:自动从 TS 类型推断 schema,让你不用写 @ApiProperty。
展开:
nest-cli.json:
json
{
"compilerOptions": {
"plugins": [
{ "name": "@nestjs/swagger", "options": { "introspectComments": true } }
]
}
}效果:
ts
// 不写 @ApiProperty 也能从类型自动生成 schema
export class CreateUserDto {
/** 邮箱 */
email: string;
/** 年龄 */
age?: number;
}注释 → description,类型 → schema 类型,? → optional。少写一半注解。
25. design-first vs code-first 怎么选?
简短答:
- 大型团队 / 多语言后端:design-first(先评审 yaml,再各自实现)
- 个人 / 小项目:code-first(边写代码边出文档)
展开:
| 维度 | design-first | code-first |
|---|---|---|
| 团队规模 | 大(5+ 人) | 小 |
| 启动速度 | 慢(要先讨论 yaml) | 快 |
| yaml 质量 | 高(评审过) | 一般(容易"脏") |
| 同步成本 | 低 | 容易代码 / 文档不同步 |
很多大团队用 hybrid:先 design 锁定 + code-first 实现 + CI 校验两者一致。
26. 接口变更怎么及时通知前端?
简短答:CI 里跑 openapi-diff 检测 breaking change,自动评论 PR;CI 自动重新生成 SDK 并发布 npm。
展开:
yaml
# GitHub Action
- name: Diff
run: |
git fetch origin main
npx -y openapi-diff origin/main:openapi.yaml openapi.yaml \
--fail-on-incompatible
- name: Generate SDK
run: npx @openapitools/openapi-generator-cli generate \
-i openapi.yaml -g typescript-axios -o sdk
- name: Publish npm
run: cd sdk && npm publish --access public前端 npm update @company/api-sdk,TypeScript 编译就告诉你哪行用了已删字段。
27. 怎么给一个老项目从 0 加 Swagger?
简短答:
- 梳理现有接口:列 URL + 参数 + 响应到表格
- 写 yaml(design-first)或后端框架挂注解(code-first)
- 挂 Swagger UI 让大家看
- CI 跑 spectral lint
- 跑 schemathesis 模糊测试:把 yaml 跟实际服务对比,找出不一致
- 逐步生成 SDK 替换前端手写的 axios 调用
展开:通常痛点是接口定义不规范(命名乱 / 无版本 / 错码不统一),加 Swagger 是重新审视 API 设计的契机。
28. Mock 服务(Prism)有什么用?
简短答:前端不用等后端——一份 yaml 直接起 Mock,返回符合 schema 的假数据。
展开:
bash
docker run -p 4010:4010 -v $PWD:/spec stoplight/prism:4 mock /spec/openapi.yaml特点:
- 自动按 schema 生成响应(或用你写的
example) - 自动按 schema 验证请求体(前端传错字段直接拒绝)
- 配合
--dynamic能生成更随机的数据 - 配合
--errors能模拟错误响应(给前端测错误处理用)
29. spectral 是干啥的?
简短答:OpenAPI 的 lint 工具,给文档质量打分。
展开:
bash
npx -y @stoplight/spectral-cli lint openapi.yaml默认规则集(spectral:oas)会检查:
- 每个 operation 是否有 summary / description
- 每个 schema 是否有 example
- path 和 method 命名是否规范
- 是否有未使用的 components
可以自定义规则,强制团队约定(如"所有 operationId 必须驼峰")。CI 里强烈推荐挂上。
30. 什么是 schemathesis?
简短答:基于 OpenAPI yaml 自动生成模糊测试——按 schema 生成各种边界值发请求,找后端 bug。
展开:
bash
pip install schemathesis
schemathesis run http://localhost:8000/openapi.json它会:
- 随机组合参数(边界值、空值、超长字符串、负数、特殊字符)
- 检查响应是否符合 yaml 里定义的 schema
- 自动发现 5xx 错误和 schema 不一致
业内最强 API 测试武器之一,CI 里跑一遍能炸出 80% 后端隐藏 bug。
🌲 专家题(⭐⭐⭐⭐⭐)
31. 如何处理"接口废弃"的 lifecycle?
简短答:
- 先标
deprecated: true(UI 画删除线 + 警告) - 在
description写明"请改用 X 接口,将于 YYYY-MM 删除" - 监控访问日志,等使用率降到 0 再真删
- 用 openapi-diff 提示"删除接口"为 breaking change
展开:
yaml
/v1/users:
get:
deprecated: true
summary: '[废弃] 请改用 /v2/users'
description: |
⚠️ 本接口将于 **2026-01-01** 删除。
请改用 [/v2/users](#operations-tag-user-listUsersV2)。32. 如何在文档里维护多版本(v1 / v2)?
简短答:两种主流:
- 路径前缀:
/v1/users、/v2/users,单文档 - 多文档:
v1/openapi.yaml、v2/openapi.yaml,UI 顶部下拉切换
展开:
yaml
# 单文档 + 路径前缀(推荐)
paths:
/v1/users:
get: { deprecated: true, ... }
/v2/users:
get: { ... }servers.url 里不要写 /v1、/v2,让路径里自己带版本,扩展性更好。
33. 接口字段变更怎么不破坏老客户端(向后兼容)?
简短答:只加不改——加新字段可,改字段名 / 删字段是 breaking。
展开:
| 操作 | 是否 breaking |
|---|---|
| 加可选 query / 字段 | ❌ 安全 |
| 加必填字段 | ✅ breaking |
| 改字段类型(int → string) | ✅ breaking |
| 删字段 | ✅ breaking |
| 改响应字段名 | ✅ breaking |
| 加新 enum 值 | ⚠️ 可能 break(严格模式下客户端会拒绝未知值) |
| 改 endpoint 路径 | ✅ breaking |
| 加新 endpoint | ❌ 安全 |
CI 里挂 openapi-diff --fail-on-incompatible,PR 自动拦截。
34. OpenAPI 3.1 比 3.0 强在哪?
简短答:完全兼容 JSON Schema 2020-12 + 新增 webhooks + nullable 改写法。
展开:
| 功能 | OpenAPI 3.0 | OpenAPI 3.1 |
|---|---|---|
| nullable | nullable: true | type: [string, "null"] |
| JSON Schema | 子集 | 完全兼容 2020-12 |
| webhooks | 没有 | 顶层 webhooks 字段 |
| examples | 单数 example 字符串 | 任意 JSON |
required 校验 | 弱 | 强 |
工具生态正在追赶;新项目 3.0 / 3.1 都行,老项目维持 3.0 即可。
35. 为什么 ChatGPT Plugins / Function Calling 用 OpenAPI?
简短答:OpenAPI 是 LLM 跟 API 对话的"翻译官"——LLM 看 yaml 就知道"我能调啥、参数咋传"。
展开:
LLM ─读─► openapi.yaml ←─导─ 你的 API
↓
"用户问'订一杯咖啡',我要调 POST /orders,body 是..."
↓
LLM 自动生成符合 schema 的请求 → 调用 API → 把响应说人话给用户新协议如 MCP (Model Context Protocol) 也借鉴了 OpenAPI 思想——OpenAPI 已经从"接口文档"进化成了"AI 时代的接口协议"。
🌳 场景题(综合考察)
36. 假设你接手一个老项目,没文档,怎么 1 周内出一份 Swagger?
思路:
- 挑工具:后端是什么栈,挂对应的 codegen(Spring → springdoc、FastAPI → 内置...)
- 打开 UI 看自动暴露的 yaml——80% 接口能马上有
- 梳理 / 补全注解(
@Schema、@Operation),让生成的 yaml 漂亮 - 跑 spectral lint,按规则修
- 挂 Mock + 给前端发 SDK 试用,反向找 bug
- 接入 CI:lint + diff,防止后续退化
37. 前端发现某接口的响应字段跟文档不一致,怎么办?
思路:
- 用 schemathesis 跑一遍,自动证明哪边对哪边错
- 如果代码对:让后端补 / 改注解,重新导出 yaml
- 如果文档对:是后端代码 bug,提 issue
- 长期:CI 里加 schemathesis 防止再发生
38. 团队里 3 个后端同时改 yaml,冲突频繁,怎么破?
思路:
- 拆文件:每模块一个
paths/xxx.yaml、schemas/xxx.yaml - 主文件
openapi.yaml只放顶层 +$ref - 用
redocly bundle在 CI 里合并 - PR 评审锁文件 owner(git CODEOWNERS)
39. 文档需要给"外部客户"看,怎么加 access control?
思路:
- 用 Redoc 部署成静态站,前面挂 Nginx Basic Auth
- 或部署到 GitHub / GitLab Pages 私有仓库
- 商业方案:Stoplight Hub、Redocly Workflows、Apifox 团队版
40. 接口文档要支持"按客户端权限显示不同内容",怎么办?
思路:
- OpenAPI 本身不支持按客户端隐藏接口
- 实战做法:
- 公开版(脱敏):删掉
/admin/*后导出 - 内部版(完整):原文件
- 公开版(脱敏):删掉
- 或用 OpenAPI 扩展字段(
x-internal: true)+ 自定义脚本过滤后再发布
终极总结:面试时的 3 句"必杀技"
当面试官问 "你怎么用 Swagger?",你这样答能拿满分:
- OpenAPI 是规范、Swagger 是工具家族;现在新项目都用 OpenAPI 3.0+。
- 我们在工程里把
openapi.yaml当源代码——CI 里跑 spectral lint + openapi-diff,自动生成 SDK 发 npm,自动生成后端 Controller 接口,配合 Prism Mock 让前后端解耦。- 核心要素是
components复用——schemas / parameters / responses / securitySchemes 全抽出来,paths 里全是$ref,加字段一处改全局生效。
把这三句背熟,你就能让面试官知道:你不只是会写文档,你懂"基于文档驱动整个工程"。
觉得题目不够?欢迎在评论里贡献你遇到的真题。 学完整套笔记 → 回到 总览。