-
OpenAPI规范
-
什么是OpenAPI规范
OpenAPI 规范(OAS),是定义一个标准的、与具体编程语言无关的RESTful API的规范。
OpenAPI 规范使得人类和计算机都能在"不接触任何程序源代码和文档、不监控网络通信"的情况下理解一个服务的作用。
简单来说,OpenAPI文档是一份机器可读的“说明书”,详细描述了你的 API 能做什么,包括:
- 服务器地址
- 有哪些可用的URL路径
- 每个路径支持哪些Action(GET, POST, PUT, DELETE 等)
- 每个Action需要哪些参数(在路径中、查询字符串中、请求头中等)
- 请求体应该是什么格式
- 每个响应可能返回的状态码和对应的数据格式
核心价值:一旦你的 API 被定义在一个标准的 OpenAPI 文档中,整个 API 生命周期中的各种工具都可以基于此文档自动完成工作,极大地提高了效率和规范性。
-
OpenAPI规范术语
-
OpenAPI 文档
用来定义或描述一个API的YAML(JSON)文档。OpenAPI 文档可以是单个文件也可以被拆分为多个文件,连接的部分由用户自行决定。固定字段:
|
字段
|
是否必须
|
描述
|
|
openapi
|
是
|
OPENAPI版本号
|
|
info
|
是
|
提供API相关的元数据,如API名称、描述、版本等
|
|
servers
|
否
|
Server地址列表
|
|
paths
|
是
|
定义API的所有请求路径及Action
|
|
components
|
否
|
提供可复用的组件定义,使用$ref引用
|
|
security
|
否
|
声明API使用的安全机制
|
|
tags
|
否
|
提供更多元数据的一系列标签。
|
|
externalDocs
|
否
|
附加文档
|
-
根级别信息
文档的元数据,描述了 API 本身的基本信息。
openapi: 3.0.3 # OpenAPI版本
info:
title: Agents API # API的名称
description: 坐席相关接口
version: 1.0.0 # API的版本
contact:
name: API支持者
email: icc2.0@msxf.com
servers:
- url: https://test.example.com/v1 # API的基础地址
description: test环境服务器
- url: https://test1.example.com/v1 # AP 的基础地址
description: test1环境服务器
paths: # 接下来会定义所有端点
components: # 接下来会定义可重用的组件
tags:
- name: agents
description: "坐席相关接口"
- name: metrics
description: "指标相关接口"
-
paths- 路径
OpenAPI文档核心,定义了 API 的所有可用URL以及每个URL支持的请求Action。
paths:
/agents:
get:
summary: 获取坐席列表
operationId: listAgents
tags:
- agents
parameters: # 参数定义
- name: limit
in: query
description: 返回结果的数量限制
schema:
type: integer
default: 10
responses: # 响应定义'200':description:成功返回坐席列表content:application/json:schema:type:arrayitems:$ref:'#/components/schemas/Agent'
post:
summary: 创建一个新坐席
operationId: createAgent
tags:
- agents
requestBody: # 请求体定义
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Agent' # 引用components定义的 Agent模型
responses: ... # 响应定义
/agents/{agentId}:
get:
summary: 通过ID获取坐席详情
operationId: getAgentById
parameters: # 包含路径参数 {agentId}-name:agentIdin:path# 参数位置:path, query, header, cookierequired:truedescription:坐席的IDschema:type:integer
responses: ...
-
components- 组件
-
可重用的对象库,用于避免重复定义。
可以在
paths中通过 $ref来引用这些组件。components:
schemas:
Agent:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
status:
type: string
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
description: 错误代码
message:
type: string
description: 错误描述
-
全局通用响应结构体
如 200、400、401、403、404、500等通用响应结构,确保全站错误格式统一。
components:
responses:
BadRequest:
description: 400 Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
invalidParams:
summary: 参数验证失败
value:
code: 400
message: 请求参数无效
Unauthorized:
description: 401 Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
tokenExpired:
summary: Token无效
value:
code: 401
message: 访问令牌已过期
Forbidden:
description: 403 Forbidden
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
insufficientPermissions:
summary: 权限不足
value:
code: 403
message: "没有操作权限"
-
OpenAPI Swagger编辑器
-
OpenAPI UI搭建
依赖安装:
yarn add express swagger-ui-express swagger-jsdoc yamljs
yarn add --dev nodemon
通过express启动UI服务:
const express = require('express');
const swaggerUi = require('swagger-ui-express');
const YAML = require('yamljs');
const app = express();
const PORT = process.env.PORT || 3000;
// 加载YAML文件
const swaggerDocument = YAML.load('./swagger.yaml');
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
app.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
console.log(`Swagger文档: http://localhost:${PORT}/api-docs`);
});
本地Swagger UI:
-
OpenAPI常见规范
- 路径命名必须使用名词复数形式:资源导向,使用名词而非动词。例如,使用 /users,而不是 /getUsers。
- 必须正确使用 HTTP 方法:
GET: 查询/获取资源
POST: 创建资源
PUT: 全量更新资源
PATCH: 部分更新资源
DELETE: 删除资源
- 路径参数必须使用大括号标明:例如 /users/{userId},并在parameters中明确定义 in: path及 required: true。
- 请求体必须使用 application/json:在 requestBody中指定 content: application/json,并引用定义的 Schema。对于文件上传,需单独使用 multipart/form-data。
- 必须定义并复用数据模型:所有请求/响应体必须在 components/schemas中定义,并在各处通过 $ref引用,避免重复定义。
- 模型命名必须使用大驼峰命名法:如 User, CreateUserRequest, UserListResponse。
- 合理使用枚举:对于固定值的字段(如状态),必须使用 enum明确列出所有可能值。
- 必须定义全局通用的响应码:在 components/responses中定义如 200、400、401、403、404、500等通用响应结构,确保全站错误格式统一。
- 每个操作必须包含清晰的摘要和描述:summary简要说明操作,description可提供更详细的上下文或注意事项。
- 必须为所有操作标记标签:使用 tags对接口进行逻辑分组,便于在生成的文档中分类展示。
-
OpenAPI优势
-
与语言无关的标准化接口描述
OpenAPI 规范使用 YAML 或 JSON 格式来描述 API,这两种格式都是机器可读的。这种标准化的描述方式不依赖于任何编程语言,使得不同的工具和系统能够基于同一份规范进行协作。
-
自动生成 API 文档
OpenAPI 规范文件可以自动生成交API 文档(如使用 Swagger UI)。文档不仅展示了 API 的URL、参数、请求和响应示例,还允许开发者直接在浏览器中尝试调用 API。
-
提升研发效率
利用OpenAPI生成工具(如 Swagger Codegen、OpenAPI Generator),可以根据规范文件自动生成多种编程语言的客户端和服务器代码。前后端可以基于API文档MOCK调试,可以加速前后端的联调进程,并减少手动编写代码可能引入的错误。
-
自动化测试
OpenAPI 规范可以用于生成自动化测试用例,验证 API 的实现是否符合规范。可以基于 OpenAPI 规范自动生成测试用例,对 API 进行属性测试,确保 API 的健壮性。
-
生态系统和工具支持
OpenAPI 拥有庞大的生态系统,包括各种工具和库,支持从设计、生成、测试到监控的整个 API 生命周期。这些工具包括:
- Swagger Editor:用于编辑和预览 OpenAPI 规范。
- Swagger UI:生成交互式 API 文档。
- Swagger Codegen:生成客户端和服务器代码。
- Mock Server:根据规范模拟 API 响应。
- Schemathesis:生成自动化测试用例
-
基于OpenAPI生成自动化用例
-
Schemathesis介绍
Schemathesis是一个基于属性测试(Property-based Testing)的API测试工具,专门用于测试符合OpenAPI/Swagger规范的Web API。
-
Schemathesis特点
-
属性测试
- 不同于传统的示例测试,不会提供明确的输入值,不验证明确逻辑。
- 自动生成大量随机但符合规范的测试数据,包括无效、意外或随机的输入,以测试API的鲁棒性和错误处理能力。
- 验证 API 的通用属性而非特定场景。
-
自动生成测试
基于OpenAPI文档自动生成测试用例,覆盖API的所有端点和参数。
st run --checks all http://api.example.com/openapi.json
-
智能断言
- 响应状态码在定义范围内
- 响应模式符合 schema
- 响应内容类型正确
- 没有服务器错误(500 Server Error)
-
Schemathesis快速开始
-
安装
pip install schemathesis
# 验证安装
schemathesis --version
-
准备yaml文件
openapi: 3.0.3
info:
title: 坐席管理 API
description: 简单的坐席管理接口示例
version: 1.0.0
contact:
name: API 支持
email: support@example.com
servers:
- url: http://localhost:8000/api/v1
description: 开发服务器
- url: https://api.example.com/v1
description: 生产服务器
paths:
/agents:
get:
summary: 获取坐席列表
description: 获取系统中所有坐席的列表,支持分页和过滤
operationId: getAgents
tags:
- 坐席管理
parameters:
- name: page
in: query
description: 页码
required: false
schema:
type: integer
minimum: 1
default: 1
example: 1
- name: limit
in: query
description: 每页数量
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 20
example: 20
- name: active
in: query
description: 过滤活跃坐席
required: false
schema:
type: boolean
example: true
responses:
'200':
description: 成功获取坐席列表
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
data:
type: object
properties:
agents:
type: array
items:
$ref: '#/components/schemas/Agent'
pagination:
$ref: '#/components/schemas/Pagination'
message:
type: string
example: "坐席列表获取成功"
examples:
success:
summary: 成功响应示例
value:
success: true
data:
agents:
- id: 1
name: "张三"
email: "zhangsan@example.com"
age: 28
active: true
createdAt: "2024-01-15T10:30:00Z"
- id: 2
name: "李四"
email: "lisi@example.com"
age: 32
active: true
createdAt: "2024-01-16T14:20:00Z"
pagination:
page: 1
limit: 20
total: 2
pages: 1
message: "坐席列表获取成功"
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
invalid-params:
summary: 参数错误示例
value:
success: false
error:
code: "INVALID_PARAMS"
message: "参数验证失败"
details:
- field: "limit"
message: "必须小于或等于100"
message: "请求参数无效"
'500':
description: 服务器内部错误
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
server-error:
summary: 服务器错误示例
value:
success: false
error:
code: "INTERNAL_ERROR"
message: "服务器内部错误"
message: "系统繁忙,请稍后重试"
components:
schemas:
Agent:
type: object
required:
- id
- name
- email
properties:
id:
type: integer
format: int64
description: 坐席唯一标识
example: 1
name:
type: string
description: 坐席姓名
minLength: 1
maxLength: 50
example: "张三"
email:
type: string
format: email
description: 坐席邮箱
example: "agent@example.com"
age:
type: integer
minimum: 0
maximum: 150
description: 坐席年龄
example: 28
active:
type: boolean
description: 是否活跃
default: true
example: true
createdAt:
type: string
format: date-time
description: 创建时间
example: "2024-01-15T10:30:00Z"
updatedAt:
type: string
format: date-time
description: 更新时间
example: "2024-01-20T08:15:00Z"
Pagination:
type: object
properties:
page:
type: integer
description: 当前页码
example: 1
limit:
type: integer
description: 每页数量
example: 20
total:
type: integer
description: 总记录数
example: 150
pages:
type: integer
description: 总页数
example: 8
Error:
type: object
properties:
success:
type: boolean
example: false
error:
type: object
properties:
code:
type: string
description: 错误代码
example: "VALIDATION_ERROR"
message:
type: string
description: 错误信息
example: "参数验证失败"
details:
type: array
items:
type: object
properties:
field:
type: string
example: "email"
message:
type: string
example: "必须是有效的邮箱格式"
message:
type: string
description: 坐席友好的错误信息
example: "请求参数无效"
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: JWT token认证
-
本地启动Mock API服务
准备nodejs环境,安装prism
yarn global add @stoplight/prism-cli
# 动态模拟接口返回
prism mock openapi.yaml --dynamic
服务启动后检查API正常返回
-
使用Schemathesis测试接口
命令行执行:
schemathesis run openapi.yaml --url=http://127.0.0.1
执行结果:
测试报告生成yaml格式:
schemathesis run openapi.yaml --url=http://127.0.0.1 --report vcr --report-vcr-path ./custom-vcr.yaml
-
Schemathesis与pytest集成
集成示例:
import schemathesis
schema = schemathesis.openapi.from_path("./openapi.yaml")
@schema.parametrize()
def test_api_code(case):
print(case)
response = case.call(url="http://127.0.0.1")
assert response.status_code is not None
assert response.status_code != 500
@schema.parametrize()
def test_api_response(case):
print(case)
case.call_and_validate(url="http://127.0.0.1")
测试报告:
浙公网安备 33010602011771号