如何使用 Flask-return 响应 400 Bad Request-RESTful.RequestParser?

How to return a 400 Bad Request response using Flask-RESTful.RequestParser?

我正在使用 Flask 和 Flask-RESTful 创建一个 API。我希望使用 flask_restful.reqparse.RequestParser() 解析传入请求的主体。在收到不正确的 JSON 后,我想 return 一个 400 Bad Request 响应。但是,我的应用程序 returning 了 500 Internal Server Error 响应。我认为 RequestParser() 应该自动处理这些响应?谁能解释一下出了什么问题?

下面是 API Resource

的代码
from flask_restful import Resource, reqparse

class Foo(Resource):

    parser = reqparse.RequestParser()
    parser.add_argument("foo",
        type=int,
        required=True,
        action='append',
        help="Request body must contain a 'foo' key which comprises a list of IDs, e.g. {'foo': [44, 3213, 532, 4312]}"
    )

    def get(self):
        data = self.parser.parse_args(strict=True)
        return {'bar': data['foo']}

当我向 API 发送一个正文为 {"err": [3, 4, 1]} 的 GET 请求时,我收到以下 500 内部服务器错误响应:

{"message": "Internal Server Error"}

而不是我在 help 参数中指定的消息。在我的日志中,我还收到以下错误消息:

KeyError: "'foo'"

我知道我可以将 data = self.parser.parse_args(strict=True) 包装在 try/except KeyError 子句中并自己处理不正确的 JSON,但我认为 Flask-RESTful 会那样做为了我?我还能尝试什么?

通过定义将传递给 RequestParser 构造函数的 APIArgument class,您可以定义自己的自定义响应。您还需要将 bundle_errors = True 传递给构造函数并通过将应用程序配置键“BUNDLE_ERRORS”设置为 True[=21 来配置 flask =]

请参阅请求解析的 error handling

import json

from flask import Flask, Response, abort
from flask_restful import Api, Resource, reqparse
from flask_restful.reqparse import Argument

app = Flask(__name__)
app.config["BUNDLE_ERRORS"] = True

api = Api(app)


class APIArgument(Argument):
    def __init__(self, *args, **kwargs):
        super(APIArgument, self).__init__(*args, **kwargs)

    def handle_validation_error(self, error, bundle_errors):
        help_str = "(%s) " % self.help if self.help else ""
        msg = "[%s]: %s%s" % (self.name, help_str, str(error))
        res = Response(
            json.dumps({"message": msg, "code": 400, "status": "FAIL"}),
            mimetype="application/json",
            status=400,
        )
        return abort(res)


class Foo(Resource):
    parser = reqparse.RequestParser(argument_class=APIArgument, bundle_errors=True)
    parser.add_argument(
        "foo",
        type=int,
        action="append",
        required=True,
        help="Request body must contain a 'foo' key which comprises a list of IDs, e.g. {'foo': [44, 3213, 532, 4312]}",
    )

    def get(self):
        data = self.parser.parse_args(strict=True)
        return {'bar': data['foo']}


api.add_resource(Foo, '/')

if __name__ == "__main__":
    app.run(port=9000, debug=True)

对于那些不想处理样板代码的人,这也适用:

通过将解析器代码移动到 get() 路由中,将使用 flask-restful 提供的默认 handle_validation_error 函数(我猜):

class Foo(Resource):
    def get(self):
        parser = reqparse.RequestParser()
        parser.add_argument('foo', required=True, action="append", help="Request body must contain a 'foo' key which comprises a list of IDs, e.g. {'foo': [44, 3213, 532, 4312]}",
        data = parser.parse_args()
        print(data.get("foo")

        # this code won't be reached, because the parser aborts the request early if foo is missing
        return {'bar': data['foo']}

这意味着您必须为每个方法提供一个单独的解析器,但我认为这通常是个好主意,而不是在路由本身中为所有方法定义它。 flask-restful 可能不打算这样做,这就是为什么它不能开箱即用的原因。

另外:请记住,如果请求来自浏览器,GET 请求不能有 json 正文,这只有在不涉及浏览器的情况下才有可能!我通常不建议这样做,因为它不是 standard-compatible.