OpenAPI 在 Django 项目中的应用
- Version:
2020-06-14
- Host:
openapi.tslow.cn
主讲人: 陶佳元 tslow.cn
接口目录:
先听个段子
说程序员最痛恨的 4 件事情
- 写注释
- 写文档
- 别人不写注释
- 别人不写文档
开始今天我们话题,接口文档
- 谁写文档:
后端工程师
- 写给谁看:
前端工程师
青铜玩家的文档
其实我们可以有更高一点的追求
- 界面美观
- 多端浏览
- 方便维护
小结一下
编写文档对于每一个普通的开发人员来说,可能算是一种 “负担”
但是在前后端分离大势所趋的今天
能够编写一份可读性高、方便传阅交流的接口文档已成为每一位后端开发者的必备技能
如果能有个工具来自动干这件事那就更棒了!
OpenAPI
返回结果说明:
参考: https://stuff.rdme.io/swagger2to3
OpenAPI3 规范简要解读
# OpenAPI 规范版本号
openapi: 3.0.3
# API 元数据信息
info:
# 服务器连接信息
servers:
# API 的分组标签
tags:
# 对所提供的 API 有效的路径和操作
paths:
# 一个包含多种纲要的元素,可重复使用组件
components:
# 声明 API 使用的安全机制
security:
# 附加文档
externalDocs:
info 部分
info:
title: xx开放平台接口文档 # 应用的名称
description: |
简短的描述信息,支持 markdown 语法。 | 表示换行,< 表示忽略换行。
version: "1.0.0" # API 文档的版本信息
termsOfService: 'http://swagger.io/terms/' # 指向服务条款的 URL 地址
contact: # 所开放的 API 的联系人信息
name: API Support # 人或组织的名称
url: http://www.example.com/support # 指向联系人信息的 URL 地址
email: apiteam@swagger.io # 人或组织的 email 地址
license: # 所开放的 API 的证书信息。
name: Apache 2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
servers 部分
servers:
- url: https://development.server.com/v1
description: 开发服务器
- url: https://{subdomain}.site.com/{version}
description: 生产服务器
variables:
subdomain:
default: production
version:
default: v2
enum:
- v1
- v2
tags 部分
tags:
- name: store
description: 宠物商店
- name: user
description: 用户操作相关
- name: pet
description: 与宠物相关的接口
externalDocs:
description: 外部文档
url: 'http://pet.docs.io'
paths 部分
# 对所提供的 API 有效的路径和操作
paths:
/pet/{petId}:
get:
tags:
- pet
summary: 简要总结,描述此路径内包含的所有操作。
description: 详细说明,用于描述此路径包含的所有操作。
operationId: getPetById # 此操作的唯一标识符
parameters: # 参数列表
- name: petId
in: path
description: 路径参数,宠物 ID。
required: true
schema:
type: integer
format: int64
responses: # 接口响应内容
'200':
description: 操作成功
content:
application/json:
schema:
type: object
properties:
id:
type: integer
format: int64
category:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
name:
type: string
example: doggie
photoUrls:
type: array
items:
type: string
tags:
type: array
items:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
status:
type: string
description: 宠物在商店的状态
enum:
- available
- pending
- sold
'400':
description: 无效的 id
'404':
description: 找不到指定的资源
components 部分
components:
schemas:
responses:
parameters:
examples:
requestBodies:
headers:
securitySchemes:
links:
callbacks:
使用 components 进行优化
paths:
'/pet/{petId}':
get:
tags:
- pet
summary: 简要总结,描述此路径内包含的所有操作
description: 详细说明,用于描述此路径包含的所有操作
operationId: getPetById # 此操作的唯一标识符
parameters: # 参数列表
- name: petId
in: path
description: pet ID
required: true
schema:
type: integer
format: int64
responses: # 响应列表
'200':
description: 操作成功
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
'400':
description: 无效的 id
'404':
description: 找不到指定的资源
components:
schemas:
Pet:
type: object
properties:
id:
type: integer
format: int64
category:
$ref: '#/components/schemas/Category'
name:
type: string
example: doggie
photoUrls:
type: array
items:
type: string
tags:
type: array
items:
$ref: '#/components/schemas/Tag'
status:
type: string
description: 宠物在商店的状态
enum:
- available
- pending
- sold
Category:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
Tag:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
POST 传参的例子
paths:
/pet:
post:
summary: 向商店中添加新的宠物
operationId: addPet
requestBody: # 请求体
description: 需要被添加进商店的 Pet 对象
required: true # 请求体是否被包含在请求中,默认值 false
content: # 请求体的内容
application/json:
schema:
$ref: '#/components/schemas/Pet'
security 部分
components:
securitySchemes:
BasicAuth:
type: http
scheme: basic
JWT:
type: http
scheme: bearer
bearerFormat: jwt
APIKey:
type: apiKey
name: api_key
in: header
security:
- BasicAuth
- APIKey
参考: https://www.jianshu.com/p/5365ef83252a
Swagger
各主流工具
OpenAPI
: 根据规范编写出yaml
或json
文件,然后使用Swagger
、ReDoc
等渲染出成品文档
RAML
: 根据规范编写出raml
文件,然后使用raml2html
等渲染出成品文档
API Blueprint
: 根据规范编写出markdown
文件,然后使用Aglio
、snowboard
等渲染出成品文档
Swagger-UI
<!DOCTYPE html>
<html>
<head>
<title>Swagger</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="//unpkg.com/swagger-ui-dist@3/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
<script>
const ui = SwaggerUIBundle({
url: "{% url schema_url %}",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "BaseLayout"
})
</script>
</body>
</html>
众多著名的框架都直接或间接的支持 Swagger 插件
- Java 领域,著名的
spring-swagger
,后更名为springfox
- django-rest-framework 搭配
drf_yasg
或coreapi
FastAPI
更是完美的原生支持,可一键生成 swagger 风格文档- ...
解决痛点: 随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象。
Django REST framework
现状:
- 官网首页推荐
core-api
,已停更
- 历史人气最高
django-rest-swagger
,已停更
- 继承者
drf-yasg
,只支持 swagger2
返回结果说明:
官方生成方法
from rest_framework.schemas import get_schema_view
urlpatterns = [
path('openapi/', get_schema_view(title='XXX API Document'), name='openapi'),
# ...
]
可惜,还没有 UI!
自己写一个也不难
from django.views.generic import TemplateView
urlpatterns = [
path('openapi/', get_schema_view(title='XXX API Document'), name='openapi'),
path('swagger-ui/', TemplateView.as_view(
template_name='swagger-ui.html',
extra_context={'schema_url': 'openapi'}
), name='swagger-ui'),
]
看似很完美,其实问题不少
- 没有
security
安全机制说明 - 没有
tags
分类 - Model 中的
verbose_name
没法反应到文档中 - Model 中的
default
没法反应到文档中 - 含有 lazytext 的话,生成文档就会出错
- 没有使用
components
进行优化 - 文档中无法体现
versioning
解决方案和思路
get_schema_view
中传参generator_class
,改写openapi.SchemaGenerator
ViewSet
设定schema
,改写openapi.AutoSchema
- 对
OpenAPIRenderer
和JSONOpenAPIRenderer
进行 monkey patch - 等待 drf 官方支持或修复,也可以尝试贡献代码
互动竞答
请问,Swagger2
同 OpenAPI3
的关系与下面几组关系中的那一组最相似?
Django
/Django REST framework
RAML
/raml2html
Python2
/Python3
Python
/Javascript