OpenAPI 在 Django 项目中的应用

主讲人: 陶佳元 tslow.cn

接口目录:


先听个段子

说程序员最痛恨的 4 件事情

  1. 写注释
  2. 写文档
  3. 别人不写注释
  4. 别人不写文档


开始今天我们话题,接口文档

  • 谁写文档: 后端工程师
  • 写给谁看: 前端工程师

青铜玩家的文档



其实我们可以有更高一点的追求

  1. 界面美观
  2. 多端浏览
  3. 方便维护


小结一下

编写文档对于每一个普通的开发人员来说,可能算是一种 “负担”
但是在前后端分离大势所趋的今天
能够编写一份可读性高方便传阅交流的接口文档已成为每一位后端开发者的必备技能
如果能有个工具来自动干这件事那就更棒了!


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 是规范/Swagger 是实现规范的工具

各主流工具

  1. OpenAPI: 根据规范编写出 yamljson 文件,然后使用 SwaggerReDoc 等渲染出成品文档


  2. RAML: 根据规范编写出 raml 文件,然后使用 raml2html 等渲染出成品文档


  3. API Blueprint: 根据规范编写出 markdown 文件,然后使用 Agliosnowboard 等渲染出成品文档


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 插件

  1. Java 领域,著名的 spring-swagger,后更名为 springfox
  2. django-rest-framework 搭配 drf_yasgcoreapi
  3. FastAPI 更是完美的原生支持,可一键生成 swagger 风格文档
  4. ...
解决痛点: 随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象。

Django REST framework

现状:

  1. 官网首页推荐 core-api,已停更


  2. 历史人气最高 django-rest-swagger,已停更


  3. 继承者 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'),
]

看似很完美,其实问题不少

  1. 没有 security 安全机制说明
  2. 没有 tags 分类
  3. Model 中的 verbose_name 没法反应到文档中
  4. Model 中的 default 没法反应到文档中
  5. 含有 lazytext 的话,生成文档就会出错
  6. 没有使用 components 进行优化
  7. 文档中无法体现 versioning



解决方案和思路

  1. get_schema_view 中传参 generator_class,改写 openapi.SchemaGenerator
  2. ViewSet 设定 schema,改写 openapi.AutoSchema
  3. OpenAPIRendererJSONOpenAPIRenderer 进行 monkey patch
  4. 等待 drf 官方支持或修复,也可以尝试贡献代码

互动竞答

请问,Swagger2OpenAPI3 的关系与下面几组关系中的那一组最相似?
  1. Django / Django REST framework
  2. RAML / raml2html
  3. Python2 / Python3
  4. Python / Javascript


谢谢大家 后会有期