flask restplus 入门

写flask接口,需要向前端提供swagger接口文档,因此需要自动生成swagger文档,可以使用 flask-restplus 库。

我们平常用的RESTful风格的开发依赖包是flask-restful,但是他不能自动生成 swagger 文档,flask-restplus可以自动生成 swagger 文档,两者之间并没有什么太大的区别,flask-restful有的东西和使用方法在flask-restplus里面都有。可能为什么叫plus就是这个原因吧。

所以之前用的 flask-restful 可以直接用 flask-restplus 直接替换。

0. 官方文档 Flask-RESTPlus 0.13.0 文档

1. 安装flask-restplus

  1. 采用如下命令安装flask-restplus

    1
    2
    3
    pip install flask-restplus==0.13.0
    # or
    easy_install flask-restplus
  2. 安装完成后导入包时报错cannot import name 'cached_property',这是因为新版的werkzeug中该模块导入发生了变化,需要手动修改 /usr/local/lib/python3.7/site-packages/werkzeug/__init__.py 文件,添加:

    1
    from werkzeug.utils import cached_property

2. 使用教程

Api()

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'}
  • api:使用 Api() 创建 api 实例,创建之后,swagger 文档默认是开启的,而且默认挂载到 / 根路由. 访问 swagger.json 的 url地址为 /swagger.json
    • version: 版本,默认值 1.0
    • title:标题
    • description:描述
    • prefix: Base URL: /v1
    • doc:默认参数 doc='/', doc 指定 swagger 文档地址挂载点的, 就是项目启动后访问的路径,如:http://localhost:8080/swagger/doc

注意, 如果是使用 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')

namespace 命名空间

1
2
3
4
5
6
7
8
from flask_restplus import Api, Resource, fields
app = Flask(__name__)

api = Api(app, version='1.0', prefix = '/'
title='AUTH API', description='A authenticate user and save cloud accounts API')

ns = api.namespace('accounts', path='/')
# ns = api.namespace('binding cloud accounts', path='/')
  • api.namespace :命名空间,很多接口都有get,post,命名空间把他们分隔开,可理解为小蓝图。
    • path:把 path 加到路由地址的前面,默认为 namesapce 的名称,最好写成 path='/'

namespace 和 蓝图

在 Flask 中构建大型 Web 项目,可以通过蓝图为路由分组,并在蓝图中添加通用的规则(url 前缀、静态文件路径、模板路径等)。

而 Flask-RESTPlus 的 class::Api 将直接挂在在蓝图下面,这么我们即利用了 Flask 的蓝图进行对功能模块分类,也可以利用 Api 的版本对 Api 版本进行管理,对于小的模块分类,我们可以利用 Api 的 namespace

1
2
3
4
5
6
from flask import Blueprint
from flask_restplus import Api

api_blueprint = Blueprint("open_api", __name__, url_prefix="/api")
api = Api(api_blueprint, version="1.0",
title="OpenApi", description="The Open Api Service")

访问 swagger 页面时,url地址为 蓝图url_prefix + /

然后,就可以创建出不同的 namespace,来编写自己的 api 代码了。而只需要在 app 工厂中注册该 blueprint,便可将自己的编写的 api 挂载到 flask app 中。

路由

添加路由有两种方式 api.add_resource() 方法或 api.route() 装饰器

1
2
3
4
5
6
7
api.add_resource(HelloWorld, '/hello', '/world')

# or

@api.route('/hello', '/world')
class HelloWorld(Resource):
pass

您还可以将部分路径作为变量匹配到资源方法。

1
2
3
4
5
6
7
api.add_resource(Todo, '/todo/<int:todo_id>', endpoint='todo_ep')

# or

@api.route('/todo/<int:todo_id>', endpoint='todo_ep')
class HelloWorld(Resource):
pass

如果请求与应用程序的任何端点都不匹配,Flask-RESTPlus 将返回一条 404 错误消息,其中包含与请求的端点密切匹配的其他端点的建议。这可以通过在您的应用程序配置中设置ERROR_404_HELP来禁用。False

参数解析

1
2
3
4
5
6
7
8
9
10
11
12
from flask_restplus import reqparse

parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate to charge for this resource')


@api.route('/with-parser/', endpoint='with-parser')
class WithParserResource(restplus.Resource):
@api.expect(parser)
def get(self):
args = parser.parse_args()
return {}

parser@api.expect(parser) 联用,会对 header 头中对应参数进行校验;并在swagger 的请求区域内添加对应的 header 参数。

使用RequestParser该类还可以免费为您提供合理的错误消息。如果参数未能通过验证,Flask-RESTPlus 将响应 400 Bad Request 和突出显示错误的响应。

{'status': 400, 'message': 'foo cannot be converted to int'}

如果请求包含解析器未定义的参数,则调用 args = parser.parse_args(strict=True) 可确保引发错误。

数据格式化

默认情况下,返回迭代中的所有字段都将按原样呈现。虽然这在处理 Python 数据结构时效果很好,但在处理对象时会变得非常令人沮丧。为了解决这个问题,Flask-RESTPlus 提供了fields模块和 marshal_with()装饰器。与 Django ORM 和 WTForm 类似,您使用该fields模块来描述响应的结构。

1
2
3
4
5
6
7
8
9
10
model = api.model('Model', {
'task': fields.String,
'uri': fields.Url('todo_ep')
})

@api.route('/a')
class A(Resource):
@api.marshal_with(model)
def get(self, **kwargs):
...

上面的示例采用一个 python 对象并准备将其序列化。marshal_with()装饰器将应用model. 从对象中提取的唯一字段是task. 该fields.Url字段是一个特殊字段,它采用端点名称并在响应中为该端点生成 URL。使用marshal_with()装饰器还可以在 swagger 规范中记录输出。您需要的许多字段类型已经包含在内。。

  • api.model:请求内容,或者是响应内容,都可以用model描述,

    • 请求内容与 @api.doc(body=model)@api.expect(model) 联用,
    • 响应内容与 @api.marshal\_with(token)@api.response(200, 'Success', model) 联用
  • model: 定义字段以及字段的描述,这些字段并不参与参数检查,而只是渲染到 api 文档上,来标记 api 将返回什么结果,以及应该怎么调用 api。

model 字段类型

model 提供了很多直接对应 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: 值必须是这个数的倍数

保留顺序

默认情况下,不会保留字段顺序,因为这会降低性能。如果你仍然需要字段顺序保存,你可以ordered=True 给一些类或函数传递一个参数来强制顺序保存:

  • 全局 Api:api = Api(ordered=True)
  • 全局 Namespace:ns = Namespace(ordered=True)
  • 本地 marshal():return marshal(data, fields, ordered=True)

3. 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from flask import Flask
from flask_restplus import Resource, Api, fields


app = Flask(__name__)
CORS(app, supports_credentials=True)

# swagger文档标题
api = Api(app, version='1.0', title='???', description='???')
app.config.update(RESTFUL_JSON=dict(ensure_ascii=False))

# 配置不同模块的命名空间
ns = api.namespace('aRisk', path='/')

# 参数解析
parser = api.parser()
parser.add_argument('rate', type=int, help='Rate to charge for this resource')

# 配置post方法需要传递的接口输入参数
mode_input = api.model('mode_input', {'input_text': fields.String(required=False, description='待查询的文本')})

# 配置返回参数
# model 子
mode_child = api.model('mode_child', {
'is_risk_source': fields.Boolean(
required=True, description='', default=False
),
'risk_source_info': fields.String(
required=True, description='',
default='预警'
)
})
# model 父 ,使用 fields.Nested(mode_child)
mode_parent = api.model('parent', {'risk_source': fields.Nested(mode_child)})


@api.route('/accounts/<string:account_name>')
class CreateAccounts(Resource):

# 下面两个效果一样:自动生成响应格式
@api.response(403, 'Not Authorized')
@api.doc(responses={403: 'Not Authorized'})

# 自动生成响应格式 并 对响应进行序列化
@api.marshal_with(mode_result, code=201)

# 下面这四个效果一样:自动生成 请求参数格式,只在 swagger ui 校验,后端并不校验
@api.param('user_id', 'The user identifier')
@api.doc(params={'account_name': 'an unique account_name of cloud account'})
# 下面这两个效果一样: 自动生成 请求参数格式 并 进行校验 其值可以通过 parser.parse_args() 获取
@api.expect(parser)
@api.doc(body=parser)
def post(self):
args = parser.parse_args()
return {'accessToken': b}

# 配置各命名空间下的接口
api.add_resource(aRisk, '/aRisk/')

这里的命名空间是 api,所以所有的装饰器都是使用 api 这个实例。


flask restplus 入门
https://flepeng.github.io/021-Python-32-框架-Flask-flask-restplus-flask-restplus-入门/
作者
Lepeng
发布于
2021年3月31日
许可协议