[flask-restful]利用reqparse解析嵌套字典结构

Flask-Restful解析请求中嵌套字典结构

先展示结果,下面再一步步解释:

from flask import Flask
from flask_restful import Api, Resource
from flask_restful.reqparse import Argument, RequestParser
from werkzeug.exceptions import BadRequest

app = Flask(__name__)
api = Api(app)

def parse_args(args, req=None, trim=True, bundle_errors=True):
    parser = RequestParser(trim=trim, bundle_errors=bundle_errors)
    parser.args = args
    return parser.parse_args(req=req)

class DictParseWrap(object):
    def __init__(self, **kwargs):
        self.unparsed_arguments = None
        self.kwargs = kwargs

    def json(self):
        return self.kwargs

class DictParseType(object):
    def __init__(self, args=None):
        self.args = args

    def __call__(self, value):
        if isinstance(value, dict):
            value = DictParseWrap(**value)
        try:
            return parse_args(self.args, req=value)
        except BadRequest as e:
            raise ValueError(getattr(e, 'data', {}).get('message') or e)

test_args = [
    Argument('args', location=['args'], type=str),
    Argument('complex_lvl_1', required=True, location=['json'], type=DictParseType([
        Argument('test_list', default=[], action='append', type=str),
        Argument('complex_lvl_2', type=DictParseType([
            Argument('test_dict', type=dict),
            Argument('test_str', type=str)
        ]))
    ]))
]

class Test(Resource):
    def post(self):
        req = parse_args(test_args)
        return req

api.add_resource(Test, '/test')


if __name__ == '__main__':
    app.run('0.0.0.0', 9000)

结果展示
在这里插入图片描述

reqparse使用简介

reqparse是flask框架下的一个拓展flask_restful提供的请求参数解析模块。
官方文档
基础的使用样例:

from flask import Flask
from flask_restful import Api, Resource
from flask_restful.reqparse import RequestParser

app = Flask(__name__)
api = Api(app)

test_args = RequestParser()
test_args.add_argument('test_str', type=str, required=True, location='json')
test_args.add_argument('test_inf', type=int, default=0, location='json')
test_args.add_argument('test_lst', action='append')

class Test(Resource):
    def post(self):
        args = test_args.parse_args()
        return args

api.add_resource(Test, '/test')

if __name__ == '__main__':
    app.run('0.0.0.0', 9000)

实现思路

从原api可以看出,reqparse只提供最基础的参数解析。想要实现嵌套结构解析,思路自然是递归思想。下面就来一步一步解决问题

1. 嵌套解析类实现

我们来看一下参数解析的源码实现,源码中Argument.convert方法实现的类型转换,下面是源码片段:

    def convert(self, value, op):
    	# some code
        try:
            return self.type(value, self.name, op)  # 实际上调用的就是type.__call__()方法
        except TypeError:
        	# some code
        	pass

因此我们实现的复杂结构类中需要重载__call__方法。
ReqParser是用列表存放Arguments的,因此我们也可以用列表存放下一层嵌套结构。
由此:

class DictParseType(object):
    def __init__(self, args=None):
        self.args = args  # 自定义的下一层参数,为Arguments的列表

    def __call__(self, value):  # 定义类型转换方法,在此种递归调用解析方法
        parser = ReqParser()
        for arg in self.args:
        	parser.add_argument(arg)
        return parser.parse_args(value)

然而这样运行会报错,原因是源码中传入的value并不是一个字典,而是request实例,因此需要封装。

2. 封装传入的字典

上面说到需要对value进行封装,代码中会用到属性request.unparsed_arguments,因此需要加入封装。

class DictParseWrap(object):
    def __init__(self, **kwargs):
        self.unparsed_arguments = None
        self.kwargs = kwargs

    def json(self):
        return self.kwargs

3. 错误处理

上面封装过后仍然存在缺陷,问题在于参数解析出错后的信息提醒:封装后的结构如果解析出错并不会返回正确的错误提醒,而是直接报错无法解析。
原因就是下面代码:

def parse(self, request, bundle_errors=False):
	...
    try:
         value = self.convert(value, operator)
     except Exception as error:
         if self.ignore:
             continue
         return self.handle_validation_error(error, bundle_errors)  # 如果解析出错则会调用handle_validation_error并返回
	...

def handle_validation_error(self, error, bundle_errors):
    """Called when an error is raised while parsing. Aborts the request
    with a 400 status and an error message

    :param error: the error that was raised
    :param bundle_errors: do not abort when first error occurs, return a
        dict with the name of the argument and the error message to be
        bundled
    """
    error_str = six.text_type(error)
    error_msg = self.help.format(error_msg=error_str) if self.help else error_str
    msg = {self.name: error_msg}

    if current_app.config.get("BUNDLE_ERRORS", False) or bundle_errors:
        return error, msg
    flask_restful.abort(400, message=msg)

由于是嵌套结构的内层解析出错后会返回一个HttpException而上层无法对此解析,所以需要额外增加错误处理,详情见顶部代码__call__方法中的错误处理。

封装添加参数并解析过程

为了添加参数的方便和解耦接口逻辑与校验逻辑,在此额外对添加参数和解析进行封装,见顶部代码parse_args函数


综上,经过源码分析,一步步地实现了文章开头的代码,使其能够解析请求中嵌套的字典结构。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
`Flask-RESTful` 是 Flask 的一个扩展,它使创建 RESTful API 变得更加容易。使用 Flask-RESTful,你可以使用 Python 类来定义资源(Resource),并使用 Flask 的路由系统来将 URL 映射到这些资源上。 Flask-RESTful 提供了一组类和方法来处理 HTTP 请求和响应,例如 `Resource`、`Api`、`reqparse` 等。其中,`Resource` 类表示一个 RESTful 资源,它封装了 HTTP 请求和响应的处理逻辑。`Api` 类表示整个 RESTful API,它可以将多个资源组合在一起,并将它们映射到 URL 上。`reqparse` 类用于解析和验证 HTTP 请求参数。 下面是一个简单的使用 Flask-RESTful 的示例: ``` from flask import Flask from flask_restful import Api, Resource, reqparse 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) ``` 在上面的示例中,`HelloWorld` 类继承自 `Resource` 类,表示一个 RESTful 资源。`get()` 方法表示处理 HTTP GET 请求的逻辑,它返回一个 JSON 响应。`api.add_resource()` 方法将 `HelloWorld` 资源映射到根 URL 上。当用户访问根 URL 时,Flask-RESTful 将自动调用 `HelloWorld` 资源的 `get()` 方法,并返回 JSON 响应。 Flask-RESTful 还支持其他 HTTP 方法,例如 POST、PUT、DELETE 等。你可以根据实际需求定义不同的资源和方法,以创建一个完整的 RESTful API。 总之,Flask-RESTful 是一个非常方便的 Flask 扩展,可以帮助你轻松地创建 RESTful API,从而提供 Web 服务和数据接口。它提供了一组类和方法,使 HTTP 请求和响应的处理变得更加简单和易于维护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kevin9436

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值