API文档自动生成工具调研
项目背景
项目的版本为python2.7,django1.11,采用的前后端交互方式是人工手写接口文档,交给前端,人工写postman接口测试,没有一套高效的自动接口文档生成和自动化测试的流程。
调研过程
//依赖库
djangorestframework==3.9.4
coreapi==2.3.3
django-rest-swagger==2.2.0
drf-yasg
实现方式:Django Rest Swagger生成api文档
实现思路:
- 修改settings配置,添加依赖库,添加swagger配置
- 修改urls.py 添加swagger文档路由,添加视图路由
- 添加视图类
- 实现序列化类
- 实现模型类
djangorestframework
这种模式在postman就能实现,没必要再自己再重新造一套。
这种接口文档的形式,可以满足需求,但还是没有swagger优雅简洁。
在实现这种模式的时候也遇到不少坑,最终没有呈现出这个效果。
django-rest-swagger:deprecated (2019-06-04)
- Django 1.8+
- Django REST framework 3.5.1+
- Python 2.7, 3.5, 3.6
核心库还是在coreapi,djangorestframework。
依赖于上述核心库的编码方式,换成了swagger的ui
非常容易实现。
drf-yasg
- Django Rest Framework: 3.10, 3.11, 3.12
- Django: 2.2, 3.0, 3.1
- Python: 3.6, 3.7, 3.8, 3.9
特征:
- 完全支持嵌套序列化器和模式
- 响应模式和描述
- 模型定义与代码生成工具兼容
- 在规范生成过程中的所有点上的定制挂钩
- JSON和YAML格式的规范
- 捆绑了最新版本的swagger-ui和redoc,用于查看生成的文档
- 架构视图是可立即缓存的
- 生成的Swagger模式可以通过Swagger -spec-validator自动验证
- 通过URLPathVersioning和NamespaceVersioning支持Django REST框架API版本化;目前不支持其他DRF或自定义版本方案
swagger模式
redoc模式
总结:提供了更加强大,更多的功能,以及在可视化ui上做得更好,本质上还是使用了djangorestframework的编码风格
问题:由于python2.7的Django Rest Framework最高只支持3.9版本,所以不兼容3.10+的要求。故可预测该方案不可行。
优缺点:
优点:
- 更规范科学的开发模式,自动生成api文档,节约大量在文档上编辑以及测试耗费的时间。
- 更直观的管理接口
缺点:
- 跟原项目的编码风格有较大出入,比如序列化过程,原项目并没有。而是直接从urls到service调用数据库。难以在项目开发规范中推广。
- 需要踩大量坑,由于原项目的python跟django版本较低,在刚尝试使用这种模式就遇到大量坑,很难预测之后会遇到什么坑。增加今后潜在的开发成本
- 需要配置session,目前还没有将项目内的接口跑通,测试成本还在增加。
- 代码侵入性较强,没办法简便的对已开发的接口进行改造,只能考虑后续加入的接口。
总结:
对于当前项目的python和django版本,当前查到的资料都脱离不开Django Rest Framework的编码风格,对于之前已经开发的接口难以引入,同事想要的是一款尽量只改变路由就能实现文档生成,而不是进行大量代码的侵入,所以并不能采用上述的库,当前探索先告一段落。
踩坑记录:
- django1的urls.py中不支持path,改为url格式(django2才支持path)
- django修改settings或修改requirement没必要将整个docker-compose重启,项目自动重启失败可以手动重启,也可以直接进docker上直接执行pip
- you cannot access body after reding from request’s data stream
一旦对序列化类修改post传入的字段时,就会出现这个问题,查阅资料是说django不能让中间件修改request的post,所以用request.data而不是用request.body 或者将body的值用变量存起来再进行操作。
4.实现GET路径传参、POST传参,需要重写schema_views.py
# -*- coding: utf-8 -*-
from rest_framework.permissions import AllowAny
from rest_framework.schemas import SchemaGenerator
from rest_framework.schemas.generators import LinkNode, insert_into
from rest_framework.renderers import *
from rest_framework_swagger import renderers
from rest_framework.response import Response
from rest_framework.views import APIView
# from rest_framework.schemas import SchemaGenerator
class MySchemaGenerator(SchemaGenerator):
def get_links(self, request=None):
# from rest_framework.schemas.generators import LinkNode,
links = LinkNode()
paths = []
view_endpoints = []
for path, method, callback in self.endpoints:
view = self.create_view(callback, method, request)
path = self.coerce_path(path, method, view)
paths.append(path)
view_endpoints.append((path, method, view))
# Only generate the path prefix for paths that will be included
if not paths:
return None
prefix = self.determine_path_prefix(paths)
for path, method, view in view_endpoints:
if not self.has_view_permissions(path, method, view):
continue
link = view.schema.get_link(path, method, base_url=self.url)
# 添加下面这一行方便在views编写过程中自定义参数.
link._fields += self.get_core_fields(view)
subpath = path[len(prefix):]
keys = self.get_keys(subpath, method, view)
# from rest_framework.schemas.generators import LinkNode, insert_into
insert_into(links, keys, link)
return links
# 从类中取出我们自定义的参数, 交给swagger 以生成接口文档.
def get_core_fields(self, view):
return getattr(view, 'coreapi_fields', ())
class SwaggerSchemaView(APIView):
_ignore_model_permissions = True
exclude_from_schema = True
# from rest_framework.permissions import AllowAny
permission_classes = [AllowAny]
# from rest_framework_swagger import renderers
# from rest_framework.renderers import *
renderer_classes = [
CoreJSONRenderer,
renderers.OpenAPIRenderer,
renderers.SwaggerUIRenderer
]
def get(self, request):
generator = MySchemaGenerator(title='接口文档',
description='''接口文档''')
schema = generator.get_schema(request=request)
# from rest_framework.response import Response
return Response(schema)
post传参
schema = AutoSchema(
manual_fields=[
coreapi.Field(name='code', required=True, location='form', description='', type='string'),
]
)
get请求
coreapi_fields=(
DocParam("token"),
)
5.“msg”: “‘CreateWhitelistGroupView’ object has no attribute ‘session’”,
需要配置swagger登录获取session