三、 Flask-Restful 介绍

Flask 和 Flask-Restful 关系

Flask-RESTful 是一个 Flask 扩展,它添加了快速构建 REST APIs 的支持。它当然也是一个能够跟你现有的ORM/库协同工作的轻量级的扩展。Flask-RESTful 鼓励以最小设置的最佳实践。如果你熟悉 Flask 的话,Flask-RESTful 应该很容易上手。理解为什么要使用 Flask-RESTful 而不是仅仅使用 Flask,可以从 Flask-RESTful 设计的几个关键优点来考虑:

  1. 代码组织
    Flask-RESTful 鼓励以资源为中心的设计,这反映了 REST 架构风格的核心思想。资源类的使用使得代码更加模块化和组织化,让不同的HTTP方法(GET, POST, PUT, DELETE等)直观地映射到类的方法上。

  2. 请求处理
    Flask-RESTful 提供了请求解析器,允许你以声明方式描述你的参数和类型,而不是手动解析和验证请求数据。这使得处理请求数据更加安全和方便。

  3. 输出序列化
    使用字段和 marshal_with 装饰器可以定义输出格式。这意味着你可以从逻辑模型分离出表现层,更容易地维护和修改返回给客户端的数据结构。

  4. 错误处理
    Flask-RESTful 有内置的错误处理功能,能够帮助你发送一致格式的错误响应。这提高了API的一致性并减少了处理错误的样板代码。

  5. 扩展性
    Flask-RESTful 为添加插件和扩展提供了清晰路径,如自定义认证、请求预处理、信号等。这些是在构建大型应用程序时必须考虑的。

  6. 社区和生态
    Flask-RESTful 基于 Flask 生态系统,意味着你可以利用 Flask 社区中的所有知识和插件,同时享受 Flask-RESTful 提供的专门优势。

虽然 Flask 本身已经非常强大和灵活,但 Flask-RESTful 是为了解决 RESTful API 特定问题而生的。它简化了解决这些问题的过程,提供了一套更适合构建和维护大型 REST API 的工具和实践。

如果你的应用程序主要是一个简单的网页应用,可能不需要 Flask-RESTful。但是,当你开始构建以数据交换为中心的服务时(比如移动应用后端或与其他系统的集成),采用 Flask-RESTful 将使得这个过程更加直观和高效。

使用教程

安装

pip install flask-restful

Flask转化成Flask-Restful

  1. 原本的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)

  1. 转化后的代码
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 需要返回结构化的响应数据时,这些工具可以帮助你确保响应体的格式是正确的,即使你的内部数据结构是复杂的对象。

使用示例说明:

  1. 定义资源字段 (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。

  2. 创建数据对象 (TodoDao):

    class TodoDao(object):
        def __init__(self, todo_id, task):
            self.todo_id = todo_id
            self.task = task
            self.status = 'active' # 这个字段不会被发送到客户端
    

    TodoDao 是一个 Python 类,代表了你想返回的数据对象。注意,虽然它有 todo_idstatus 两个字段,但这些字段并没有在 resource_fields 中定义,因此不会被包含在最终的响应中。

  3. 使用 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_idstatus),但它们不会出现在序列化后的 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>')

  • 17
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值