使用 Flask-RESTPlus 构建 Swagger API 文档

上篇文章主要介绍了 flask-restplus 构建 rest api 的主要用法,这篇文章将介绍如何使用此扩展构建 文档, 实现 文档跟着代码走.

开启文档

上一篇文章已经提到, 文档默认是开启的,而且默认挂载到 / 根路由, 在使用 api = Api(app) 的时候, 会有个默认参数 doc='/', 这里的 doc 是指定文档地址挂载点的, 还有个参数为 version, 它的值默认为 1.0, 表示当前文档的版本, 使用 title 参数可以描述文档标题. 示例代码如下所示:

1
2
3
4
5
6
7
8
9
10
from flask import Flask
from flask_restplus import Api, Resource

app = Flask(__name__)
api = Api(app, doc='/doc', version='1.0', title='学习 Flask-RESTPlus')

@api.route('/hello')
class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}

此时 doc 参数设置为 /doc, 那访问 /doc 就可以访问到文档:

文档

如上所示,文档的标题为 学习 Flask-RESTPlus, 版本为 1.0, 并且默认渲染资源路由. 有一点需要注意, 如果是使用 Flask 实例 初始化的 Api 对象, 那么文档的 base_url/, 但是如果是使用 蓝图 初始化, 并且在初始化蓝图对象时设置了 url_prefix 参数, 那么文档的 base_url 是挂载到蓝图的 url_prefix 上的. 以下代码表示将文档挂载到蓝图根路由, 即 /api/v1.

1
2
api_v1 = Blueprint('api_v1', __name__, url_prefix='/api/v1')
api = Api(api_v1, version='1.0', title='API')

通过命名空间渲染文档

上一小节中的文档渲染出来为默认的命名空间, 一个命名空间对应一个资源, 它的文档也将列在其命名空间下, 以下为示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from flask_restplus import Api, Namespace, Resource

app = Flask(__name__)
api = Api(app, doc='/', version='1.0', title='待办列表API')

todos = Namespace('todos', description='待办列表')
practice = Namespace('practice', description='练习')

api.add_namespace(todos)
api.add_namespace(practice)

@todos.route('')
class TODOList(Resource):
    def get(self):
        return []

@practice.route('')
class Practice(Resource):
    def get(self):
        return []

渲染出的文档结果如下所示, 可以看到渲染出的文档一目了然:

对象模型

使用 flask-restplus 提供的 fileds模型, 将它与 ORM 对象或者自定义类对应,可以很方便渲染响应和请求参数. 其实它是类似于将对象进行序列化的工具, 在请求的时候, 通过模型判断请求参数, 在响应的时候通过模型进行序列化. 同时文档也可以通过模型来渲染需要的请求参数以及响应数据结构.

基础用法

1
2
3
4
5
model = api.model('Model', {
    'name': fields.String,
    'address': fields.String,
    'date_updated': fields.DateTime(dt_format='rfc822'),
})

以上创建了一个名为 Model 拥有三个字段的模型, 创建完成后可以使用 api.expect(model) 来渲染请求参数文档, 使用 api.response(mode=model) 来渲染响应数据文档, 这个我们接下来讲.

渲染效果

flask-restplus 提供的 api.expect 用来渲染请求参数, todos.response 用来渲染响应数据,有多个响应, 就可以多次使用此装饰器, 以下代码渲染出的文档效果如下:

1
2
3
4
5
6
@todos.route('')
class TODOList(Resource):
    @todos.expect(model)
    @todos.response(code=200, model=model, description='请求成功响应')
    def get(self):
        return []

如图所示, model 模型已经被自动转化为 json 格式的数据. 同时渲染出的 Swagger 文档还提供了发送请求功能, 也就是右上角的 Try it out. 如果想要响应模型列表, 可以这样写 todos.response(code=200, model=[model], description='请求成功响应'), 如果我只需要模型中的部分字段只用于响应, 另一部分字段只用于请求,这就需要使用到 字段属性 了.

模型字段

模型提供了很多直接对应 Python 数据类型的字段, 以下为部分常用字段:

  • fields.Integer 表示数字
  • fields.String 表示字符串
  • fields.Datetime 表示日期
  • fields.List 表示列表
    • fields.List(fields.String) 将列表项字段传入其中即可
  • etc

除了这些常规字段,如果一个字段对应的值是另一个模型, 类似与数据库 一对一 或者 一对多 的关系, 可以使用 fields.Nested 字段. 使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
model = api.model('Model', {
    'name': fields.String,
    'address': fields.String,
    'date_updated': fields.DateTime(dt_format='rfc822'),
})

# 直接引用一个模型
another_model = api.model('AnotherModel', {
    'model': fields.Nested(model)
})

# 引用模型列表
another_model = api.model('AnotherModel', {
    'model': fields.List(fields.Nested(model))
})

# 或者
another_model = api.model('AnotherModel', {
    'model': fields.Nested(model, as_list=True)
})

还有一些其他字段以及高阶用法, 可以参考文档

字段属性

类似于 fields.String 或者 fields.Integer 的字段都拥有自己的属性, 如果使用过 SQLalchemy 的话, 它与模型类的字段属性是同一个性质, 可以对属性赋值, 来更加细致的描述当前属性:

  • required 用来设置是否为必传参数, 默认为 False
  • readonly 用来设置只读, 表示只有在响应的时候才会出现, 默认为 None
  • example 用力设置示例, 默认为 None
  • description 属性用来描述当前字段信息, 默认为 None

部分字段属性同时也提供了参数校验功能, 对于 fields.String:

  • enum 一个列表, 字段的值只能是其中的某一个
  • min_length 字段最小长度
  • max_length 字段最大长度
  • pattern 使用正则判断

对于 fields.Integer fields.Float:

  • min 最小值
  • max 最大值
  • exclusiveMin: 如果为 True, 则左开区间, 默认为 False
  • exclusiveMax: 如果为 True, 则右开区间, 默认为 False
  • multiple: 值必须是这个数的倍数

开启参数验证

使用 @todos.expect(model, validate=True) 就可以开启参数验证, 参数验证出错会抛出 400 异常.

使用 parser 进行参数验证

如上所述, 模型是以 json 的方式进行解析的, 如果传入模型进行参数判断, 那也只能校验 json 参数, 所以在 expect 方法中, 除了根据模型, 也可以根据 parser 校验参数. 校验参数的介绍可以看上一篇文章, 或者直接查看文档, 使用了 parser 后, 就可以对 表单, 查询字符串, 请求头, 数据类型 等进行校验.

url 参数渲染

对于 @api.route('/') 中的参数, 可以使用 @api.param('id', description='id') 进行渲染. 此装饰器一般用来装饰 资源类, 上面使用模型的方式一般用来装饰 资源类方法, 也可以装饰 资源类, 比如 404 响应, 表示当前类下的每个方法都可能会返回 404.

总结

使用两篇文章分别介绍了 构建API构建API文档, 但只是介绍了常用的一些使用方式, 对于更详细和高阶的用法还是需要阅读文档. 其实 对象模型 是用来进行序列化的, 我使用起来感觉不太好用, 就只用来生成文档, 对于简单的参数也会用它进行校验. 对于序列化, 我比较喜欢使用 marshmallow, 它的用法和 对象模型 十分相似, 感兴趣可以去看下文档.