Flask-Restful 介绍
Flask 和 Flask-Restful 关系
Flask-RESTful 是一个 Flask 扩展,它添加了快速构建 REST APIs 的支持。它当然也是一个能够跟你现有的ORM/库协同工作的轻量级的扩展。Flask-RESTful 鼓励以最小设置的最佳实践。如果你熟悉 Flask 的话,Flask-RESTful 应该很容易上手。理解为什么要使用 Flask-RESTful 而不是仅仅使用 Flask,可以从 Flask-RESTful 设计的几个关键优点来考虑:
-
代码组织:
Flask-RESTful 鼓励以资源为中心的设计,这反映了 REST 架构风格的核心思想。资源类的使用使得代码更加模块化和组织化,让不同的HTTP方法(GET, POST, PUT, DELETE等)直观地映射到类的方法上。 -
请求处理:
Flask-RESTful 提供了请求解析器,允许你以声明方式描述你的参数和类型,而不是手动解析和验证请求数据。这使得处理请求数据更加安全和方便。 -
输出序列化:
使用字段和 marshal_with 装饰器可以定义输出格式。这意味着你可以从逻辑模型分离出表现层,更容易地维护和修改返回给客户端的数据结构。 -
错误处理:
Flask-RESTful 有内置的错误处理功能,能够帮助你发送一致格式的错误响应。这提高了API的一致性并减少了处理错误的样板代码。 -
扩展性:
Flask-RESTful 为添加插件和扩展提供了清晰路径,如自定义认证、请求预处理、信号等。这些是在构建大型应用程序时必须考虑的。 -
社区和生态:
Flask-RESTful 基于 Flask 生态系统,意味着你可以利用 Flask 社区中的所有知识和插件,同时享受 Flask-RESTful 提供的专门优势。
虽然 Flask 本身已经非常强大和灵活,但 Flask-RESTful 是为了解决 RESTful API 特定问题而生的。它简化了解决这些问题的过程,提供了一套更适合构建和维护大型 REST API 的工具和实践。
如果你的应用程序主要是一个简单的网页应用,可能不需要 Flask-RESTful。但是,当你开始构建以数据交换为中心的服务时(比如移动应用后端或与其他系统的集成),采用 Flask-RESTful 将使得这个过程更加直观和高效。
使用教程
安装
pip install flask-restful
Flask转化成Flask-Restful
- 原本的Flask代码
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/')
def hello_world():
return jsonify({'hello': 'world'})
if __name__ == '__main__':
app.run(debug=True)
- 转化后的代码
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class HelloWorld(Resource):
def get(self):
return {'hello': 'world'}
api.add_resource(HelloWorld, '/')
if __name__ == '__main__':
app.run(debug=True)
这段代码使用了 Flask 的 jsonify 函数来保证将 Python 的字典转换为 JSON 响应。在这个简单的例子中,使用 Flask-RESTful 与仅使用 Flask 的差别不大,因为都是在处理一个简单的 GET 请求。然而,Flask-RESTful 的真正优势在于处理更复杂的场景和多个端点,以及更细粒度的控制,如请求解析和数据序列化。
参数定义 parse_args功能
Argument Parsing(参数解析)是 Flask-RESTful 库中的一个功能,它简化了请求数据的验证过程。在构建 API 的时候,经常需要从客户端收集和验证数据,这通常涉及到检查查询字符串或POST表单编码的数据。虽然 Flask 框架本身提供了获取请求数据的便捷方式,但是验证这些数据是否符合要求依然可能是一个麻烦事。(主要用于验证输入数据)
Flask-RESTful 通过内置的 reqparse 模块,使得请求数据的验证工作变得更加简单。reqparse 模块的使用方式与 argparse(Python 标准库中用于解析命令行参数的模块)非常相似,但是更适用于 web 请求的上下文。
下面是一个使用 reqparse 的简单例子:
首先,你需要从 flask_restful
导入 reqparse
:
from flask_restful import reqparse
然后,创建一个 RequestParser
的实例,并通过 add_argument
方法添加你想要验证的参数:
parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate to charge for this resource')
在这个例子中,我们添加了一个名为 ‘rate’ 的参数,并指定它的类型为 int
(整数)。help
参数是当验证失败时返回的错误消息。
使用 parse_args()
方法解析请求中的参数,并返回一个字典:
args = parser.parse_args()
注意,与 argparse
模块不同的地方在于,reqparse.RequestParser.parse_args()
返回的是一个 Python 字典,而不是 argparse
模块中的自定义数据结构。
如果请求中的参数没有通过验证,Flask-RESTful 会返回一个含有错误信息的 400 Bad Request 响应。例如,如果 ‘rate’ 参数不能被转换为整数,用户会收到如下的错误响应:
{'status': 400, 'message': 'foo cannot be converted to int'}
reqparse
模块还提供了一些内置的转换函数,例如 inputs.date()
和 inputs.url()
,可以用来验证特定格式的数据。
如果你希望确保只解析你定义过的参数,并且当请求包含任何未定义的参数时抛出错误,你可以在调用 parse_args()
方法时传入 strict=True
:
args = parser.parse_args(strict=True)
这样,如果请求中含有任何额外的参数,就会抛出错误,而不是无视这些额外的参数。这在 API 设计中有助于保持参数的清晰和一致性。
一个更复杂的例子
在 Flask-RESTful 中,api.add_resource
是用来将资源(通常是类视图)绑定到特定的端点(URL)。资源可以理解为 Flask-RESTful 中对 API 终点的抽象表示,而每个资源都可以处理一或多个 HTTP 方法(如 GET, POST, PUT, DELETE 等)。
reqparse
用于请求参数的解析和验证,是资源内部用于处理输入数据的一个工具。在定义资源时,你可能会使用 reqparse
来确保传入的请求数据是有效和可用的。
下面是一个简化的例子来展示这两者是如何结合工作的:
假设你有一个资源 Todo
,并且你想要添加到你的 API 中:
from flask_restful import Resource, Api, reqparse
# 定义资源
class Todo(Resource):
# 使用 reqparse 验证输入参数
parser = reqparse.RequestParser()
parser.add_argument('task', type=str, required=True, help='Task cannot be blank!')
# 定义 GET 方法
def get(self, todo_id):
# 这里可以添加获取 todo_id 对应的任务的逻辑
pass
# 定义 POST 方法
def post(self):
# 使用 parser 解析参数
args = self.parser.parse_args()
task = args['task']
# 这里可以添加创建新任务的逻辑
pass
# 创建 API 对象
api = Api(app)
# 将 Todo 资源绑定到 '/todos/<int:todo_id>' 端点
api.add_resource(Todo, '/todos/<int:todo_id>')
在这个例子中,api.add_resource(Todo, '/todos/<int:todo_id>')
告诉 Flask-RESTful 在 URL 路径 ‘/todos/int:todo_id’ 处提供 Todo
资源,并处理相应的 HTTP 请求。这个资源内部使用 reqparse
来解析和验证传入的数据。当客户端向这个 API 发送请求时,Todo
类中定义的方法将会被调用,并且 reqparse
将会根据定义处理请求携带的参数。
通过将资源注册到 API 并定义如何解析请求数据,开发者能够创建清晰、可维护且功能齐全的 RESTful API。
fields 和 marshal_with模块
reqparse 和fields 模块以及marshal_with() 装饰器在 Flask-RESTful 中分别用于处理不同的任务。reqparse 主要用于处理和验证输入数据(即请求数据),而 fields 模块和 marshal_with() 装饰器用于格式化和输出数据(即响应数据)。让我们详细探讨一下它们各自的作用:
Flask-RESTful 的 fields
模块和 marshal_with()
装饰器用于定义和格式化返回的 JSON 数据结构。当你的 API 需要返回结构化的响应数据时,这些工具可以帮助你确保响应体的格式是正确的,即使你的内部数据结构是复杂的对象。
使用示例说明:
-
定义资源字段 (
resource_fields
):from flask_restful import fields, marshal_with resource_fields = { 'task': fields.String, 'uri': fields.Url('todo_ep') }
这里,
resource_fields
是一个字典,定义了返回 JSON 对象的格式。task
对应于一个字符串字段,而uri
是一个特殊的字段类型fields.Url
,这个类型将根据提供的端点名称 (todo_ep
) 自动生成一个 URL。 -
创建数据对象 (
TodoDao
):class TodoDao(object): def __init__(self, todo_id, task): self.todo_id = todo_id self.task = task self.status = 'active' # 这个字段不会被发送到客户端
TodoDao
是一个 Python 类,代表了你想返回的数据对象。注意,虽然它有todo_id
和status
两个字段,但这些字段并没有在resource_fields
中定义,因此不会被包含在最终的响应中。 -
使用
marshal_with()
装饰器:class Todo(Resource): @marshal_with(resource_fields) def get(self, **kwargs): return TodoDao(todo_id='my_todo', task='Remember the milk')
Todo
资源类中定义了一个get
方法,该方法通过marshal_with(resource_fields)
装饰器来指定返回数据的格式。当这个方法被调用时,它返回一个TodoDao
实例。marshal_with()
将接收这个实例,并根据resource_fields
中定义的规则来格式化数据,最后将数据序列化为 JSON 格式返回给客户端。
在这个例子中,响应仅包含 task
字段和 uri
字段。尽管 TodoDao
对象中还包含其他字段(如 todo_id
和 status
),但它们不会出现在序列化后的 JSON 响应中,因为 resource_fields
中没有定义这些字段。
总结:
fields
模块允许你声明响应数据的结构。marshal_with()
是一个装饰器,它使用fields
声明的结构来转换 Python 对象到客户端所接受的 JSON 格式。- 只有在
fields
定义中声明过的属性才会被包含在最终的序列化响应中。 fields.Url
是一个特殊类型的字段,用于生成指向特定资源端点的 URL。
这种设计模式有助于创建一致且易于理解的 API,因为它将内部数据的表示与外部 API 的表示分离开来,使得后端的改动不会直接影响到客户端。
一个更复杂的例子
from flask_restful import Resource, Api, reqparse
# 定义资源
class Todo(Resource):
# 使用 reqparse 验证输入参数
parser = reqparse.RequestParser()
parser.add_argument('task', type=str, required=True, help='Task cannot be blank!')
# 定义 GET 方法
def get(self, todo_id):
# 这里可以添加获取 todo_id 对应的任务的逻辑
pass
# 定义 POST 方法
def post(self):
# 使用 parser 解析参数
args = self.parser.parse_args()
task = args['task']
# 这里可以添加创建新任务的逻辑
pass
# 创建 API 对象
api = Api(app)
# 将 Todo 资源绑定到 '/todos/<int:todo_id>' 端点
api.add_resource(Todo, '/todos/<int:todo_id>')