OpenAPI 规范验证
确保 API 规范的正确性和一致性
为什么要验证?
OpenAPI 规范验证能够:
- ✅ 语法检查: 确保 YAML/JSON 格式正确
- 📋 规范合规: 符合 OpenAPI 3.0/3.1 标准
- 🔍 引用完整: 检查 $ref 引用的有效性
- 🎯 最佳实践: 遵循 API 设计最佳实践
- 🚨 早期发现: 在开发早期发现问题
验证工具对比
| 工具 | 特点 | 适用场景 |
|---|---|---|
| Redocly CLI | 功能全面,规则丰富 | 企业级项目 |
| Spectral | 可扩展,自定义规则 | 需要定制化验证 |
| OpenAPI Generator | 集成在生成流程中 | 代码生成时验证 |
| Swagger Editor | 在线实时验证 | 快速验证 |
| openapi-spec-validator | Python 库,轻量级 | 脚本集成 |
Redocly CLI 验证
基础验证
bash
# 验证单个文件
redocly lint openapi.yaml
# 验证并显示详细信息
redocly lint openapi.yaml --format=stylish
# 只显示错误(忽略警告)
redocly lint openapi.yaml --errors-only
# 生成报告
redocly lint openapi.yaml --format=json > report.json配置验证规则
yaml
# redocly.yaml
lint:
extends:
- recommended # 使用推荐规则集
rules:
# 信息完整性
info-contact: error
info-license: error
info-description: error
# 操作规范
operation-summary: error
operation-description: warning
operation-operationId: error
operation-operationId-unique: error
operation-tags: error
operation-tag-defined: error
# 参数验证
parameter-description: warning
path-params-defined: error
path-not-include-query: error
# 响应验证
operation-success-response: error
operation-4xx-response: warning
# 安全性
operation-security-defined: error
security-defined: error
# 组件使用
no-unused-components: warning
# 命名规范
operation-operationId-valid-in-url: error
path-segment-plural: warning自定义规则
yaml
# custom-rules.yaml
rules:
# 中文描述检查
chinese-description:
description: "描述必须包含中文说明"
message: "{{property}} 应该包含中文描述"
given:
- "$.info"
- "$.paths.*.*"
then:
field: description
function: pattern
functionOptions:
match: "[\u4e00-\u9fa5]"
# 响应示例检查
response-examples:
description: "响应必须包含示例"
given: "$.paths.*.*.responses.*.content.application/json"
then:
field: example
function: truthy
# 分页参数检查
pagination-parameters:
description: "列表接口必须支持分页"
given: "$.paths.*.get[?(@.operationId =~ /list|get.*s$/i)]"
then:
field: parameters
function: schema
functionOptions:
schema:
type: array
contains:
type: object
properties:
name:
enum: ["page", "size", "limit", "offset"]Spectral 验证
安装和使用
bash
# 安装
npm install -g @stoplight/spectral-cli
# 基础验证
spectral lint openapi.yaml
# 使用特定规则集
spectral lint openapi.yaml --ruleset=.spectral.yaml
# 输出格式
spectral lint openapi.yaml --format=junit > report.xmlSpectral 配置
yaml
# .spectral.yaml
extends:
- spectral:oas
- spectral:asyncapi
rules:
# 覆盖默认规则
info-contact: error
info-description: error
# 自定义规则
my-custom-rule:
description: 自定义规则描述
given: "$.paths.*.*"
severity: error
then:
- field: "summary"
function: truthy
- field: "description"
function: truthy
# 路径规则
paths-kebab-case:
description: 路径必须使用 kebab-case
given: "$.paths"
then:
field: "@key"
function: pattern
functionOptions:
match: "^/[a-z0-9]+(?:-[a-z0-9]+)*(?:/[a-z0-9]+(?:-[a-z0-9]+)*)*$"
# 模型命名规则
schema-names-pascal-case:
description: Schema 名称必须使用 PascalCase
given: "$.components.schemas"
then:
field: "@key"
function: pattern
functionOptions:
match: "^[A-Z][a-zA-Z0-9]*$"自定义函数
javascript
// .spectral/functions/customFunction.js
module.exports = (input, options, context) => {
const { path } = context;
// 自定义验证逻辑
if (input && !input.includes('example')) {
return [
{
message: `${path} 缺少示例`,
path: [...context.path]
}
];
}
return [];
};
// 使用自定义函数
// .spectral.yaml
functions:
- customFunction
rules:
require-examples:
given: "$.paths.*.*.responses.*.content.*.examples"
then:
function: customFunctionOpenAPI Generator 验证
命令行验证
bash
# 验证规范
openapi-generator-cli validate -i openapi.yaml
# 跳过验证生成代码
openapi-generator-cli generate \
-i openapi.yaml \
-g spring \
-o ./generated \
--skip-validate-spec
# 使用建议模式(显示改进建议)
openapi-generator-cli validate -i openapi.yaml --recommend编程方式验证
java
// Java 示例
import org.openapitools.codegen.validations.oas.OpenApiEvaluator;
public class ApiValidator {
public static void validateSpec(String specPath) {
OpenApiEvaluator evaluator = new OpenApiEvaluator();
List<ValidationMessage> messages = evaluator.validate(specPath);
messages.forEach(msg -> {
System.out.println(msg.getSeverity() + ": " + msg.getMessage());
});
if (messages.stream().anyMatch(m -> m.getSeverity() == Severity.ERROR)) {
throw new ValidationException("API 规范验证失败");
}
}
}Python 验证工具
openapi-spec-validator
python
# 安装
pip install openapi-spec-validator
# 命令行使用
openapi-spec-validator openapi.yaml
# Python 代码使用
from openapi_spec_validator import validate_spec_url
from openapi_spec_validator.readers import read_from_filename
# 验证文件
spec_dict, spec_url = read_from_filename('openapi.yaml')
validate_spec_url(spec_dict, spec_url)
# 验证 URL
validate_spec_url('https://api.example.com/openapi.json')自定义验证脚本
python
import yaml
import json
from openapi_spec_validator import validate_spec
def validate_api_spec(filename):
"""验证 OpenAPI 规范"""
# 读取文件
with open(filename, 'r', encoding='utf-8') as f:
if filename.endswith('.yaml') or filename.endswith('.yml'):
spec = yaml.safe_load(f)
else:
spec = json.load(f)
# 基础验证
try:
validate_spec(spec)
print("✅ OpenAPI 规范验证通过")
except Exception as e:
print(f"❌ 验证失败: {e}")
return False
# 自定义验证
errors = []
# 检查必需字段
if 'info' not in spec or 'contact' not in spec['info']:
errors.append("缺少联系信息")
# 检查操作 ID
for path, methods in spec.get('paths', {}).items():
for method, operation in methods.items():
if method in ['get', 'post', 'put', 'delete', 'patch']:
if 'operationId' not in operation:
errors.append(f"{method.upper()} {path} 缺少 operationId")
if 'summary' not in operation:
errors.append(f"{method.upper()} {path} 缺少 summary")
# 检查组件使用
components = spec.get('components', {})
schemas = components.get('schemas', {})
used_schemas = set()
# 递归查找引用
def find_refs(obj):
if isinstance(obj, dict):
if '$ref' in obj:
ref = obj['$ref']
if ref.startswith('#/components/schemas/'):
schema_name = ref.split('/')[-1]
used_schemas.add(schema_name)
for value in obj.values():
find_refs(value)
elif isinstance(obj, list):
for item in obj:
find_refs(item)
find_refs(spec)
# 查找未使用的组件
for schema_name in schemas:
if schema_name not in used_schemas:
errors.append(f"未使用的 schema: {schema_name}")
# 输出结果
if errors:
print("\n⚠️ 自定义验证发现问题:")
for error in errors:
print(f" - {error}")
return False
return True
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
validate_api_spec(sys.argv[1])
else:
print("Usage: python validate.py <openapi-spec-file>")VS Code 集成
扩展配置
json
// .vscode/settings.json
{
"openapi.validate": true,
"openapi.linting": {
"enable": true,
"rules": {
"info-contact": "error",
"info-license": "error",
"operation-operationId": "error",
"operation-summary": "error",
"oas3-schema": "error"
}
},
"yaml.schemas": {
"https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/schemas/v3.0/schema.json": "openapi.yaml"
}
}任务配置
json
// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "Validate OpenAPI",
"type": "shell",
"command": "redocly lint ${file}",
"problemMatcher": {
"owner": "openapi",
"pattern": {
"regexp": "^(.+):(\\d+):(\\d+)\\s+(error|warning)\\s+(.+)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
},
"group": {
"kind": "test",
"isDefault": true
}
}
]
}CI/CD 集成验证
GitHub Actions
yaml
# .github/workflows/validate-api.yml
name: Validate OpenAPI
on:
pull_request:
paths:
- 'api/**/*.yaml'
- 'api/**/*.yml'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Validate OpenAPI Definition
uses: char0n/swagger-editor-validate@v1
with:
definition-file: api/openapi.yaml
- name: Spectral Linting
uses: stoplightio/spectral-action@v0.8.10
with:
file_glob: 'api/**/*.yaml'
- name: Generate Validation Report
if: always()
run: |
npx @redocly/cli lint api/openapi.yaml \
--format=json > validation-report.json
- name: Upload Report
if: always()
uses: actions/upload-artifact@v3
with:
name: validation-report
path: validation-report.json
- name: Comment PR
if: failure()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '❌ OpenAPI 验证失败,请查看详细报告'
})Pre-commit Hook
yaml
# .pre-commit-config.yaml
repos:
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.23.0
hooks:
- id: check-jsonschema
name: Validate OpenAPI spec
files: ^api/.*\.(yaml|yml|json)$
args: ["--schemafile", "https://spec.openapis.org/oas/3.0/schema/2021-09-28"]
- repo: local
hooks:
- id: openapi-validator
name: OpenAPI Validator
entry: npx @redocly/cli lint
language: system
files: ^api/.*\.(yaml|yml)$
pass_filenames: true验证报告生成
HTML 报告
javascript
// generate-report.js
const fs = require('fs');
const { exec } = require('child_process');
const util = require('util');
const execPromise = util.promisify(exec);
async function generateValidationReport(specFile) {
// 运行各种验证工具
const results = {
timestamp: new Date().toISOString(),
spec: specFile,
validators: {}
};
// Redocly 验证
try {
const { stdout } = await execPromise(`redocly lint ${specFile} --format=json`);
results.validators.redocly = JSON.parse(stdout);
} catch (error) {
results.validators.redocly = { error: error.message };
}
// Spectral 验证
try {
const { stdout } = await execPromise(`spectral lint ${specFile} --format=json`);
results.validators.spectral = JSON.parse(stdout);
} catch (error) {
results.validators.spectral = { error: error.message };
}
// 生成 HTML 报告
const html = `
<!DOCTYPE html>
<html>
<head>
<title>OpenAPI Validation Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.error { color: #d32f2f; }
.warning { color: #f57c00; }
.success { color: #388e3c; }
.validator { margin: 20px 0; padding: 20px; border: 1px solid #ddd; }
pre { background: #f5f5f5; padding: 10px; overflow-x: auto; }
</style>
</head>
<body>
<h1>OpenAPI Validation Report</h1>
<p>Generated: ${results.timestamp}</p>
<p>Spec: ${results.spec}</p>
${Object.entries(results.validators).map(([name, result]) => `
<div class="validator">
<h2>${name}</h2>
<pre>${JSON.stringify(result, null, 2)}</pre>
</div>
`).join('')}
</body>
</html>
`;
fs.writeFileSync('validation-report.html', html);
console.log('Report generated: validation-report.html');
}
// 使用
generateValidationReport('api/openapi.yaml');最佳实践
1. 分层验证策略
yaml
# 三层验证策略
validation:
# 第一层:语法验证
syntax:
- yaml-lint
- json-schema
# 第二层:规范验证
specification:
- openapi-3.0-compliance
- references-validation
# 第三层:质量验证
quality:
- naming-conventions
- documentation-completeness
- examples-presence
- security-definitions2. 团队验证标准
yaml
# team-validation-rules.yaml
rules:
# 必须通过的规则(CI 阻断)
blocking:
- info-contact
- info-license
- operation-operationId-unique
- path-params-defined
- oas3-valid-schema
# 应该修复的规则(警告)
non-blocking:
- operation-description
- parameter-description
- response-examples
# 逐步改进的规则
progressive:
- operation-performance-hint
- schema-optimization3. 验证自动化
bash
#!/bin/bash
# validate-all.sh
echo "🔍 开始 OpenAPI 规范验证..."
# 语法检查
echo "1. YAML 语法检查"
yamllint api/openapi.yaml || exit 1
# 规范验证
echo "2. OpenAPI 规范验证"
openapi-generator-cli validate -i api/openapi.yaml || exit 1
# 质量检查
echo "3. 质量检查"
redocly lint api/openapi.yaml || exit 1
# 安全检查
echo "4. 安全检查"
spectral lint api/openapi.yaml --ruleset=.spectral-security.yaml || exit 1
echo "✅ 所有验证通过!"总结
完善的验证策略确保:
- ✅ 规范正确: 符合 OpenAPI 标准
- 📋 质量保证: 遵循最佳实践
- 🔍 早期发现: 减少后期修复成本
- 🚀 自动化: 集成到开发流程
通过合理的验证工具组合,构建高质量的 API 规范。