欢迎文档
Flask-RESTPlus 是对 Flask 的扩展,它增加了对快速开发 REST API 的支持。Flask-RESTPlus 鼓励以最小的设置来实现功能的开发。如果你熟悉 Flask,那么会很容易就能上手 Flask-RESTPlus。Flask-RESTPlus 中提供了大量的装饰器和工具来描述你的 API,并以文档化的形式将这些接口展现出来(通过Swagger来实现)。
1. 版本兼容性
支持 2.7+以上版本的python
2. 安装
1. 使用pip方式进行安装
1 | $ pip install flask-restplus |
2. 使用easy_install方式进行安装
1 | $ easy_install flask-restplus |
3. 教程文档
3.1 安装
使用pip方式进行安装
1 | pip install flask-restplus |
开发版本可以通过GitHub的方式进行下载安装
1 | git clone https://github.com/noirbizarre/flask-restplus.git |
Flask-RESTPlus支持Python版本是2.7,3.3,3.4,3.5.同样也支持 PyPy and PyPy3.
3.2 快速开始
前提条件:了解Flask,已经安装好Flask和Flask-RESTPlus。
3.2.1 初始化
1 | from flask import Flask |
或者使用工厂模式进行初始化:
1 | from flask import Flask |
3.2.2 最简单的API
1 | from flask import Flask |
保存该文件为api.py.使用python解释器运行该文件,并且开启debug模式方便调试。
1 | $ python api.py |
警告:不要在生产环境开启debug模式 |
使用curl命令访问该接口:
1 | $ curl http://127.0.0.1:5000/hello |
或者使用浏览器,输入地址:http://127.0.0.1:5000/hello进行访问
3.2.3 资源路由
1 | from flask import Flask, request |
使用curl命令访问:
1 | $ curl http://localhost:5000/todo1 -d "data=Remember the milk" -X PUT |
或者使用python的Requests模块进行访问
1 | >>> from requests import put, get |
Flask-RESTPlus返回任何可迭代的类型,它会将该返回值转换成响应对象(response),包括
原始的 Flask 响应对象。Flask-RESTPlus 还提供了设置响应码和响应头的功能.
1 | class Todo1(Resource): |
3.2.4 端点
可以向 Api 对象的add_resource()方法或 route()装饰器中传入多个 URL,这样每个 URL 都将会路由到该资源上:
1 | api.add_resource(HelloWorld, '/hello', '/world') # HelloWorld为下方的class类名 |
也可以将 URL 中的部分内容设置成变量,以此来匹配资源方法
1 | api.add_resource(HelloWorld, '/todo/<int:todo_id>', endpoint='todo_ep') |
注意:如果一个请求(request)与应用的任何端点都不匹配,那么 Flask-RESTPlus 将会返回一个 404 错误信息,并给出其他与所请求端点最匹配的建议信息。不过,我们可以通过在程序配置中设置 ERROR_404_HELP 为 False 来关闭该功能。
1 | app.config['ERROR_404_HELP'] = False |
3.2.5 参数解析
Flask-RESTPlus 内置支持对请求数据的验证,这一功能是通过使用一个类似于 argparse 的库来实现的
1 | from flask_restplus import reqparse |
注意:parse_args()返回的是一个 Python 字典,而不是自定义数据结构。
使用 RequestParser 类还能获取完整的错误信息。如果一个参数未验证通过,Flask-RESTPlus 将响应一个 400 错误请求,以及一个高亮错误信息的响应。示例程序如下:
1 | #!/usr/bin/env python |
其中,parser.add_argument(‘rate’, type=int,required=True,help=’Rate to charge for this resource’)表示,参数名为 rate,数据类型为 int,请求时必须发送此参数,如果验证不通过时将会返回 help 指定的信息。
运行程序并使用 curl 进行访问,几种情况的验证结果如下:
提供 rate 值,但不是 int 型(验证不通过)
提供 rate 值,且是 int 型(验证通过)
不提供 rate 值(验证不通过)
注:使用curl访问的时候,除了需要传递data参数值外,还需要传递rate参数值才行.当然data参数值也可以进行验证.
参数 strict=True 的含义:若传递了未定义的参数则报错.
1 | args = parser.parse_args(strict=True) |
3.2.6 数据格式化
默认情况下,在返回的可迭代对象中的所有字段都会原样返回,但是当涉及到对象时将会变得非常棘
手.Flask-RESTPlus 提供了 fields 模块和marshal_with()装饰器来解决返回的是对象的问题.
1 | from collections import OrderedDict |
此功能的作用如下:若返回的是一个对象,可以通话该装饰器,格式化该对象要返回的数据,数据表查询比较常用
顺序保留
默认情况下,字段顺序并未得到保留,因为它会损耗性能。不过,如果你确实需要保留字段顺序,那么可以向类或函数传入一个 ordered=True 的参数项,以此强制进行顺序保留:
Api 全局保留:api = Api(ordered = True)
Namespace 全局保留:ns = Namespace(ordered=True)
marshal()局部保留:return marshal(data, fields, ordered=True)
marshal()局部保留的例子如下:
1 | from collections import OrderedDict |
3.2.7 完整例子
1 | #!/usr/bin/env python |
4.响应编组
4.1 基本使用
1 | from flask_restplus import Resource, fields |
下方结果对上述代码稍作修改,主要是为了体现@api.marshal_with(model, envelope=’resource’)中envelope的作用
1 | C:\Users\Administrator>curl http://127.0.0.1:5000/todo |
注:@api.marshal_with()装饰器的作用等价于如下:
1 | returtn api.marshal(被装饰对象,model,envelope='resource'),200 |
4.2 重命名属性
该功能主要作用是同一个变量,在代码内部使用一个同一个变量名,提供给用户时显示的是另一个变量名
1 | model = { |
完整示例如下:
1 | from flask import Flask, request |
注意看上述代码的示例:代码中使用的变量名是private_name,返回给用户的结果中显示的变量名是name,具体如下:
1 | C:\Users\Administrator>curl http://127.0.0.1:5000/todo |
也可以指定为 lambda 表达式或者其他可调用的语句
1 | model = { |
还可以利用 attribute 来访问嵌套的属性
1 | model = { |
4.3 默认值
为对象中不存在但是返回值存在的指定默认值,重命名例子中的age就是,如下所示:
1 | model = { |
4.4 自定义字段及多值情况
该功能通俗点来说是根据值得不同返回对应的不同的结果
如上述的部分代码:
1 | class UrgentItem(fields.Raw): |
当返回的TodoDao对象中,flags的值不同,返回的参数priority和status值也会根据上述两个class的返回结果而不同.
当flags=1时,返回的结果是:
1 | C:\Users\Administrator>curl http://127.0.0.1:5000/todo |
当flags=2时,返回的结果是:
1 | C:\Users\Administrator>curl http://127.0.0.1:5000/todo |
4.5 URL和其他字段
该返回对象值增加额外的字段
1 | class RandomNumber(fields.Raw): |
注意:以上代码经过修改,跟官网文档不一样,主要是output()中多了一个ordered=None,不然不加这个会报错:TypeError: output() got an unexpected keyword argument ‘ordered’
可以把def output(self,key,obj,ordered=None)看成是一个固定写法,只修改其返回值即可.
返回结果是:
1 | C:\Users\Administrator>curl http://127.0.0.1:5000/todo |
默认情况下,fields.Url 返回的是一个相对于根路径的相对 URI,不过也可以返回绝对路径,以及地址的schema(协议)等.
1 | model = { |
4.6 复杂结构
提供一个扁平的结构,而 marshal()则会按照定义的规则将其转换成一个嵌套结构
1 | >>> from flask_restplus import fields, marshal |
注意:上述示例中的 address 字段其实并不存在于数据对象中,但是任何子字段都能够直接从对象中访问该属性,就像它们并不是嵌套关系一样。
4.7 列表字段
将字段解组成列表
1 | from flask_restplus import fields, marshal |
4.8 嵌套字段
使用 Nested 来将嵌套的数据结构解组,并对其进行适当的渲染
1 | from flask_restplus import fields, marshal |
该示例使用两个 Nested 字段。Nested 构造函数接受一个字段组成的字典,然后将其渲染成一个子 fields.input 对象。Nested 构造函数和嵌套字典(上个例子)之间的重要不同点是:属性的上下文环境。在本例中,billing_address是一个复杂的对象,它拥有自己的字段,而传入到嵌套字段中的上下文环境是子对象,而不是原始的 data 对象。也就是说:data.billing_address.addr1处于该范围,而在前一示例中,data.addr1 则是位置属性。记住:Nested 和List 对象为属性创建了一个新的作用范围。
默认情况下,当子对象为 None 时,将会为嵌套字段生成一个包含默认值的对象,而不是 null 值。可以通过传入 allow_null 参数来修改这一点
使用 Nested 和 List 来编组更复杂对象的列表
1 | user_fields = api.model('User', { |
4.9 api.model()工厂
model()工厂允许我们实例化并注册模型到我们的 API 和命名空间(Namespace)中
1 | my_fields = api.model('MyModel', { |
4.9.1 clone实现复制
Model.clone()方法使得我们可以实例化一个增强模型,它能够省去我们复制所有字段的麻烦
跟python的类继承有点类似,注意以下两种不同方式的写法。
如下面的例子,child除了有其自身的age之外,还有克隆parent过来的name
1 | parent = Model('Parent', { |
示例代码如下:
1 | #!/usr/bin/env python |
结果如下:
1 | C:\Users\sandu>curl http://127.0.0.1:5000/hello |
Api/Namespace.clone 也会将其注册到 API
1 | parent = api.model('Parent', { |
示例代码:
1 | #!/usr/bin/env python |
结果如下:
1 | C:\Users\sandu>curl http://127.0.0.1:5000/hello |
4.9.2 api.inherit 实现多态
Model.inherit()方法允许我们以“Swagger”方式扩展模型,并开始解决多态问题
1 | parent = api.model('Parent', { |
示例代码:
1 | from flask import Flask |
运行结果:
1 | C:\Users\sandu>curl http://127.0.0.1:5000/hello |
使用浏览器访问(http://127.0.0.1:5000/)的话会看到Models中有两个
Api/Namespace.clone 会将 parent 和 child 都注册到 Swagger 模型定义中
1 | parent = Model('Parent', { |
示例代码:
1 | from flask import Flask |
运行结果:
1 | C:\Users\sandu>curl http://127.0.0.1:5000/hello |
本例中的class字段只有在其不存在于序列化对象中时,才会以序列化的模型名称进行填充。
Polymorph字段允许你指定Python类和字段规范的映射关系:
1 | mapping = { |
注:这段还未看明白啥意思,具体咋操作的,结果如何,留待以后研究。
4.10 自定义字段
自定义输出字段使得我们可以在无需直接修改内部对象的情况下,进行自定义的输出结果格式化操作。我们只需让类继承Raw,并实现format()方法:
1 | class AllCapsString(fields.Raw): |
示例代码:
1 | from flask import Flask |
运行结果如下:
1 | C:\Users\sandu>curl http://127.0.0.1:5000/hello |
也可以使用schema_format、schema_type和schema_example来指定生成的类型和例子:
1 | class MyIntField(fields.Integer): |
实际代码:
1 | from flask import Flask |
4.11 跳过值为None的字段
将可选参数skip_none设置为True
1 | >>> from flask_restplus import Model, fields, marshal_with |
可以看到,address_1和address_2被marshal_with()跳过了。address_1被跳过是因为它的值为None,而address_2被跳过是因为get()返回的字典中并不包含address_2这个key。
4.12跳过嵌套字段中的None字段
1 | >>> from flask_restplus import Model, fields, marshal_with |
4.13 使用JSON Schema定义模型
注:这个还不知道具体咋使用的
1 | address = api.schema_model('Address', { |
5.请求解析
Flask-RESTPlus的请求解析接口reqparse是模仿argparse接口实现的。它的设计目的是对Flask中flask.request对象上的任何变量提供简单和统一的访问方式。
5.1 基本参数
如下是一个简单示例,在flask.Request.values字典中查找两个参数:一个整数和一个字符串:
1 | from flask_restplus import reqparse |
默认参数类型是unicode字符串。在Python3中为str类型,而在Python2中为unicode。
如果指定了help变量的值,那么在解析请求时,如果出现了类型错误,那么会将它渲染为错误信息。如果未指定help信息,那么默认的行为是返回类型错误信息本身。
默认情况下,参数不是必需的。并且,如果请求中提供的某些参数不是RequestParser的部分内容,那么这些参数将会被忽略。
请求解析器中声明的参数,如果在请求中并未设置这些参数值,那么它们将会默认设置为None。
5.2 必须参数
如果需要确保某个参数必须提供,那么可以在调用add_argument()时传入required=True的参数项:
1 | parser.add_argument('name', required=True, help="Name cannot be blank!") |
如果请求中未提供该参数,那么将会返回错误信息
5.3 多值和列表
为某个key接受多个值以构成列表,那么可以传入action=’append’:
1 | parser.add_argument('name', action='append') |
此时的查询格式如下所示:
1 | curl http://api.example.com -d "name=bob" -d "name=sue" -d "name=joe" |
而程序中获取到的参数如下所示:
1 | args = parser.parse_args() |
如果期望一个逗号分隔的列表,那么可以使用action=’split’:
1 | parser.add_argument('fruits', action='split') |
此时的查询格式如下所示:
1 | curl http://api.example.com -d "fruits=apple,lemon,cherry" |
而程序中获取到的参数如下所示:
1 | args = parser.parse_args() |
5.4 其他目标
如果期望参数一旦被解析,就将其存储为其他名字,那么可以使用dest参数:
1 | parser.add_argument('name', dest='public_name') |
5.5 参数位置
默认情况下,RequestParser尝试从flask.Request.values和flask.Request.json中解析值。
在add_argument()中使用location参数来指定获取值的其他位置。可以使用flask.Request上的任何变量,例如:
1 | # 仅仅在POST body中查找 |
当location=’json’时只能使用type=list
使用location=’form’既能验证表单数据,又能为表单字段文档化
5.6 参数多位置
为location参数赋一个列表就能为参数指定多个位置:
1 | parser.add_argument('text', location=['headers', 'values']) |
当指定多个参数位置时,那么所有指定位置的参数将会组成一个MultiDict。其中,列表中最后位置处的参数将会优先存储在结果集中。
如果参数位置列表中包含headers位置,那么参数名将变成对大小写敏感,并且必须匹配它们的标题大小写名称(见str.title())。
指定location=’headers’(而不是作为列表的某个元素)将保留大小写不敏感的特性。
5.7 高级类型处理
inputs模块中提供了一些常用的类型处理方法,如下:
- boolean()用于广泛的布尔值处理
- ipv4()和ipv6()用于IP地址
- date_from_iso8601()和datetime_from_iso8601()用于ISO8601 date和datetime处理
只需要使用它们作为type参数的值即可:
1 | parser.add_argument('flag', type=inputs.boolean) |
我们也可以编写自己的输入类型:
1 | def my_type(value): |
5.8 解析器继承
可以编写一个父解析器,父解析器中包含所有共同的参数,然后利用copy()方法来扩展解析器。另外,也可以利用replace_argument()来覆写父解析器中的任何参数,或者利用remove_argument()完全移除父解析器中的某个参数。例如:
1 | from flask_restplus import reqparse |
5.9 文件上传
为了利用RequestParser处理文件上传问题,我们需要将location变量值设置为files,并设置type值为FileStorage。如下所示:
1 | from werkzeug.datastructures import FileStorage |
5.10 错误处理
RequestParser处理错误的默认方式是在第一个错误产生时中断。当我们拥有需要花费一定时间来处理的参数时,这种方式是有好处的。然而,通常来说,将所有产生的错误都绑定在一起,然后同时一次性返回给客户端,这种方式则更加友好。这种方式既可以在Flask应用级别指定,也可以在特定的RequestParser实例级别指定。为了调用一个包含错误绑定选项的RequestParser,需要传入参数bundle_errors。例如:
1 | from flask_restplus import reqparse |
示例代码:
1 | from flask import Flask |
1 | # 设置bundle_errors=True的情况 |
应用级别的配置key为“BUNDLE_ERRORS”。例如:
1 | from flask import Flask |
BUNDLE_ERRORS是一个全局设置,它将覆盖每个RequestParser实例中的bundle_errors选项值
4.11 错误消息
每个字段的错误消息都可以通过在Argument(也包括RequestParser.add_argument)中使用help参数来自定义。
如果没有提供help参数,那么该字段的错误消息将会是类型错误本身的字符串表示。如果提供了help参数,那么错误消息将会是help参数的值。
help可能包含一个插入的符号{error_msg},它将会替换成类型错误的字符串表示。这种方式能够实现自定义错误消息,同时保留原始的错误消息。如下所示:
1 | from flask_restplus import reqparse |
实际代码:
1 | from flask import Flask |
1 | # 生成的错误信息跟官方提供的有点不一样,具体如下: |
6.错误处理
6.1 http异常处理
Werkzeug httpException将自动正确序列化,并重用description属性。
1 | from werkzeug.exceptions import BadRequest |
返回如下的400错误代码和信息:
1 | { |
若是设置的有自定义的错误信息,比如这样的:
1 | from werkzeug.exceptions import BadRequest |
错误显示信息将会是这样的:
1 | { |
还可以 通过向异常提供数据属性,可以将附加属性附加到输出。
1 | from werkzeug.exceptions import BadRequest |
错误显示信息将会是这样的:
1 | { |
6.2 Flask错误助手
abort帮助程序将错误正确包装到httpexception中,以便具有相同的行为。
1 | from flask import abort |
返回如下的400错误代码和信息:
1 | { |
如果在abort中指定错误代码和错误信息,比如:
1 | from flask import abort |
错误显示信息将会是这样的:
1 | { |
6.3 Flask-RESTPlus错误助手
errors.abort()和namespace.abort()的工作方式与原始flask.abort()类似,但它还将向响应添加关键字参数。
1 | from flask_restplus import abort |
返回如下的400错误代码和信息:
1 | { |
如果在abort中指定错误代码和错误信息,比如:
1 | from flask import abort |
错误显示信息将会是这样的:
1 | { |
6.4 @api.errorhandler装饰器
@api.errorhandler 装饰器允许您以与flask/blueprint@errorhandler decorator相同的方式为给定的异常(或从中继承的任何异常)注册特定的处理程序。
1 |
|
OpenAPI 2.0规范要求“noresultfound”错误(带说明)。错误句柄函数中的docstring将作为说明输出到swagger.json中。
或者使用如下的这种方式:
1 |
|
在这个例子中,函数文档中的:raise:的信息将会自动提取出来,并且响应400将正确记录
它还允许在不使用参数的情况下重写默认错误处理程序
1 |
|
默认情况下,flask restplus将在错误响应中返回一条消息。如果需要自定义响应作为错误,而不需要消息字段,则可以通过在应用程序配置中将error_include_message设置为false来禁用该响应。
也可以在命名空间上注册错误处理程序。在命名空间上注册的错误处理程序将重写在API上注册的错误处理程序。
1 | ns = Namespace('cats', description='Cats related operations') |
7.字段掩码
flask restplus支持部分对象获取(aka.字段屏蔽)通过在请求中提供自定义头。
默认情况下,请求头是X-Fields字段,但可以使用restplus_mask_header参数进行更改。
7.1 语法
只需提供一个由逗号分隔的字段名列表,可以选择用括号括起来。
1 | # 以下两种写法作用相等 |
要指定嵌套字段掩码,只需在字段名后面的括号中提供它:
1 | mask = '{name, age, pet{name}}' |
嵌套规范适用于嵌套对象或对象列表:
1 | # Will apply the mask {name} to each pet in the pets list. |
有一个特殊的星形标记,意思是“所有剩余字段”。它只允许指定嵌套筛选:
1 | # Will apply the mask {name} to each pet in the pets list and take all other root fields without filtering. |
7.2 用法
默认情况下,每次使用api.marshal或@api.marshal_with时,如果存在头,则会自动应用掩码。
每次将@api.marshal_与decorator一起使用时,头都将作为一个swager参数公开。
因为一旦全局头可以使您的Swagger规范更加冗长,它就不允许公开它。您可以通过将restplus_mask_swagger设置为false来禁用此行为。
还可以指定一个默认掩码,如果找不到头掩码,则将应用该默认掩码。
1 | class MyResource(Resource): |
默认掩码也可以在模型级别处理:
1 | model = api.model('Person', { |
要覆盖默认掩码,您需要提供另一个掩码或将*作为掩码传递。
8. Swagger文档
Swagger API文档是自动生成的,可以从API的根URL获得。您可以使用@api.doc()装饰器配置文档。
8.1 @api.doc()装饰器文档
@api.doc()装饰器允许您在文档中包含其他信息。
可以在class类中或者method中
1 |
|
8.2 自动文件模型
使用model()、clone()和inherit()实例化的所有模型都将自动记录在您的swager规范中。
inherit()方法将在swager模型定义中同时注册父级和子级:
1 | parent = api.model('Parent', { |
上述配置将产生以下Swagger 定义:
1 | "Parent": { |
8.3@api.marshal_with()装饰器
此装饰器的工作方式与原始marshal_with()装饰器类似,不同之处在于它记录了这些方法。可选参数代码允许您指定预期的HTTP状态代码(默认为200)。可选参数as_list允许您指定对象是否作为列表返回。
1 | resource_fields = api.model('Resource', { |
Api.marshal_list_with()装饰器严格相等于Api.marshal_with(fields, as_list=True)()。
1 | resource_fields = api.model('Resource', { |
8.4 @api.expect()装饰器
@api.expect()装饰器允许您指定预期的输入字段。它接受一个可选的布尔参数validate,指示是否应验证有效负载。通过将restplus_validate配置设置为true或将validate=true传递给API构造函数,可以全局自定义验证行为。
如下两个例子的作用是相等的:
使用@api.expect()装饰器
1
2
3
4
5
6
7
8
9resource_fields = api.model('Resource', {
'name': fields.String,
})
class MyResource(Resource):
def get(self):
pass使用api.doc()装饰器
1
2
3
4
5
6
7
8
9resource_fields = api.model('Resource', {
'name': fields.String,
})
class MyResource(Resource):
def get(self):
pass
可以将列表指定为预期输入:
1 | resource_fields = api.model('Resource', { |
可以使用RequestParser定义预期的输入:
1 | parser = api.parser() |
可以在特定终结点上启用或禁用验证:
1 | resource_fields = api.model('Resource', { |
通过配置进行的应用程序范围验证示例:
1 | app.config['RESTPLUS_VALIDATE'] = True |
构造函数在应用程序范围内验证的示例:
1 | api = Api(app, validate=True) |
8.5 @api.response()装饰器文档
@api.response()装饰器允许您记录已知的响应,它是@api.doc(responses=’’的快捷方式)
如下两个示例是相等的:
1 |
|
您可以选择指定响应模型作为第三个参数:
1 | model = api.model('Model', { |
@api.marshal_with()装饰器自动记录响应:
1 | model = api.model('Model', { |
您可以指定在不知道响应代码的情况下发送的默认响应:
1 |
|
8.6 @api.route()装饰器
您可以使用api.route()的doc参数提供类范围的文档。此参数接受与api.doc()装饰器相同的值。
以下两种装饰器例子作用相同:
使用@api.doc()
1
2
3
4
5
class MyResource(Resource):
def get(self, id):
return {}使用@api.route()
1
2
3
4
class MyResource(Resource):
def get(self, id):
return {}
8.7 fields参数
……未完待续……