如何在Flask上返回400(错误请求)?

75
我创建了一个简单的Flask应用程序,并且我正在读取Python的响应。
response = requests.post(url,data=json.dumps(data), headers=headers ) 
data = json.loads(response.text)

现在我的问题是,在某些条件下,我想返回一个400或500的消息响应。到目前为止,我是这样做的:
abort(400, 'Record not found') 
#or 
abort(500, 'Some error...') 

这会在终端上打印出消息。

enter image description here

但是在API响应中,我一直收到500错误响应。

enter image description here

代码的结构如下所示:
|--my_app
   |--server.py
   |--main.py
   |--swagger.yml

server.py 文件中有以下代码:

from flask import render_template
import connexion
# Create the application instance
app = connexion.App(__name__, specification_dir="./")
# read the swagger.yml file to configure the endpoints
app.add_api("swagger.yml")
# Create a URL route in our application for "/"
@app.route("/")
def home():
    """
    This function just responds to the browser URL
    localhost:5000/

    :return:        the rendered template "home.html"
    """
    return render_template("home.html")
if __name__ == "__main__":
    app.run(host="0.0.0.0", port="33")

而且 main.py 包含了我在 API 端点中使用的所有函数。

E.G:

def my_funct():
   abort(400, 'Record not found') 

当调用my_funct时,我在终端上打印出Record not found,但在API的响应中却没有这个信息,而是始终收到500错误消息。

2
展示完整的路由代码 -- 从你目前分享的内容中,我无法判断你是没有“返回”异常,还是有其他逻辑错误。 - Doobeh
2
说句实话,“404”是“记录未找到”的规范代码。请参阅HTTP状态码列表 - David Cain
@Doobeh 我刚刚更新了问题。 - Luis Ramon Ramirez Rodriguez
7个回答

132
你有多种选择:
最基本的选择:
@app.route('/')
def index():
    return "Record not found", 400

如果你想要访问头部信息,你可以获取响应对象:
@app.route('/')
def index():
    resp = make_response("Record not found", 400)
    resp.headers['X-Something'] = 'A value'
    return resp

或者你可以更明确一些,不仅仅返回一个数字,而是返回一个状态码对象。
from flask_api import status

@app.route('/')
def index():
    return "Record not found", status.HTTP_400_BAD_REQUEST

进一步阅读:

你可以在这里阅读更多关于前两个的内容:关于响应(Flask快速入门)
以及第三个的内容:状态码(Flask API指南)


40

我喜欢使用flask.Response类:

from flask import Response


@app.route("/")
def index():
    return Response(
        "The response body goes here",
        status=400,
    )

flask.abortwerkzeug.exceptions.abort的一个包装器,它只是一个辅助方法,使抛出HTTP异常更容易。在大多数情况下这很好,但对于restful API而言,明确返回响应可能会更好。


5
有人可以详细说明一下Response类相较于只返回一个字符串/代码元组的优势吗? - Adam Hughes
1
@AdamHughes 在这种情况下它们是相同的,因为在 Flask 调用元组时会调用 Response。Response 还允许您设置内容类型和 MIME 类型,但我认为您可以在不导入 response 的情况下完成此操作。Miguel Grinberg 有 这篇文章 可以看看。 - frogg
@AdamHughes 自描述代码。个人而言,我发现明确地说“这是一个带有这段文本和这个状态的响应”要比仅返回一个裸字符串和一个神奇数字更加清晰明了。不必了解Flask API也能理解它的作用;这一点立即显而易见。(任何熟悉HTTP的人都会将400状态与"Bad Request"文本联系起来,但这是一个更普遍的原则,而不仅限于特定的数字。) - Luc
@AdamHughes 自描述代码。个人而言,我觉得直接说“这是一个响应,带有这个文本和这个状态”要比只返回一个裸字符串和一个神奇数字更加明确。你不需要了解Flask API也能理解这段代码的作用;它立即就能显而易见。熟悉HTTP的人也会认识到400状态和错误请求文本,但这是一个更普遍的原则,而不仅仅是针对这个特定的数字。 - undefined

7

这是我几年前编写的一个Flask应用程序的一些代码片段,其中包含了一个400响应示例。

import werkzeug
from flask import Flask, Response, json
from flask_restplus import reqparse, Api, Resource, abort
from flask_restful import request
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

api = Api(app)

parser = reqparse.RequestParser()
parser.add_argument('address_to_score', type=werkzeug.datastructures.FileStorage, location='files')

class MissingColumnException(Exception):
    pass

class InvalidDateFormatException(Exception):
    pass

@api.route('/project')
class Project(Resource):

    @api.expect(parser)
    @api.response(200, 'Success')
    @api.response(400, 'Validation Error')
    def post(self):
        """
        Takes in an excel file of addresses and outputs a JSON with scores and rankings.
        """
        try:
            df, input_trees, needed_zones = data.parse_incoming_file(request)

        except MissingColumnException as e:
            abort(400, 'Excel File Missing Mandatory Column(s):', columns=str(e))

        except Exception as e:
            abort(400, str(e))

        project_trees = data.load_needed_trees(needed_zones, settings['directories']['current_tree_folder'])

        df = data.multiprocess_query(df, input_trees, project_trees)
        df = data.score_locations(df)
        df = data.rank_locations(df)
        df = data.replace_null(df)
        output_file = df.to_dict('index')
        resp = Response(json.dumps(output_file), mimetype='application/json')
        resp.status_code = 200

    return resp

@api.route('/project/health')
class ProjectHealth(Resource):

    @api.response(200, 'Success')
    def get(self):
        """
        Returns the status of the server if it's still running.
        """
        resp = Response(json.dumps('OK'), mimetype='application/json')
        resp.status_code = 200

    return resp

6

您可以返回一个元组,其中第二个元素是状态(400或500)。

from flask import Flask
app = Flask(__name__)


@app.route('/')
def hello():
    return "Record not found", 400

if __name__ == '__main__':
    app.run()

Python调用API的示例:

import requests

response = requests.get('http://127.0.0.1:5000/')

response.text
# 'This is a bad request!'

response.status_code
# 400

5
我认为你正确地使用了abort()函数。我怀疑这里的问题是一个错误处理程序捕获了400错误,然后出现了500错误。有关Flask错误处理的更多信息,请参见此处
例如,以下内容将把400更改为500错误:
@app.errorhandler(400)
def handle_400_error(e):
    raise Exception("Unhandled Exception")

如果您没有进行任何错误处理,可能是来自connexion框架,尽管我不熟悉这个框架。

4
你可以直接使用@app.errorhandler修饰器。
例如:
 @app.errorhandler(400)
    def your_function():
        return 'your custom text', 400

1
抛出一个异常,让默认的错误处理程序来处理它。参见:
from werkzeug.exceptions import NotFound


@bp.get('/account/<int:account_id>')
def show(account_id):
    if None:
        raise NotFound()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接