如何处理 Flask 应用程序中的错误

作者选择了 自由和开源基金作为 写给捐款计划的一部分接受捐款。

介绍

Flask是一个轻量级的Python网络框架,提供用于在Python语言中创建Web应用程序的有用的工具和功能。

当您开发 Web 应用程序时,您将不可避免地遇到应用程序行为与预期相反的情况,您可能会错误编写变量,滥用循环,或构建一个如果声明以一种引发Python例外的方式,例如在声明功能之前呼叫函数,或者只是寻找一个不存在的页面。

在本教程中,您将构建一个小型 Web 应用程序,展示如何处理开发 Web 应用程序时遇到的常见错误,您将创建自定义错误页面,使用 Flask 调试程序来解决异常,并使用日志来跟踪应用程序中的事件。

前提条件

步骤 1 – 使用Flask Debugger

在此步骤中,您将创建一个具有几个错误的应用程序,并在没有调试模式的情况下运行,以查看应用程序的响应方式。

当您的编程环境启用并安装Flask时,请打开名为app.py的文件,在您的flask_app目录中进行编辑:

1nano app.py

将下列代码添加到app.py文件中:

1[label flask_app/app.py]
2from flask import Flask
3
4app = Flask(__name__)
5
6@app.route('/')
7def index():
8    return render_template('index.html')

在上面的代码中,您首先从flask包中导入Flask类别,然后创建一个名为appflask应用程序实例. 您使用@app.route()装饰器创建一个名为index()的视图函数,该函数将render_template()函数称为返回值,从而返回一个名为index.html的模板。 这个代码有两个错误:第一是您没有导入render_template()函数,第二是index.html模板文件不存在。

保存并关闭文件。

接下来,使用下面的命令(在 Windows 上,使用导出而不是设置使用导出来告知 Flask 关于应用程序的FLASK_APP环境变量):

1export FLASK_APP=app

然后使用flask run命令运行应用程序服务器:

1flask run

您将在您的终端中看到以下信息:

1[secondary_label Output]
2 * Serving Flask app 'app' (lazy loading)
3 * Environment: production
4   WARNING: This is a development server. Do not use it in a production deployment.
5   Use a production WSGI server instead.
6 * Debug mode: off
7 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

此输出提供了以下信息:

  • Flask 應用程式在服務(app.py在這種情況下)
  • 環境,這是「生產」這裡。 警告訊息強調,這個伺服器不是為了生產部署。 您正在使用這個伺服器進行開發,所以您可以忽略這個警告,但要了解更多信息,請參閱 Flask 文档上的 開發選項頁面。 您也可以檢查此 使用 Gunicorn 的 Flash 部署教程,或 使用 uWSGI 的這個伺服器,或者您可以使用 DigitalOcean App Platform 來部署您的 Flask 應用程式,如下 [如何使用 Gunicorn 應用程式開發 Flask 應用程式

现在,访问使用您的浏览器的索引页面:

1http://127.0.0.1:5000/

你会看到一个消息,看起来如下:

1[secondary_label Output]
2Internal Server Error
3
4The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

这是500内部服务器错误(LINK0),这是一个服务器错误响应,表明服务器在应用程序代码中遇到内部错误。

在终端中,你会看到以下输出:

 1[secondary_label Output]
 2[2021-09-12 15:16:56,441] ERROR in app: Exception on / [GET]
 3Traceback (most recent call last):
 4  File "/home/abd/.local/lib/python3.9/site-packages/flask/app.py", line 2070, in wsgi_app
 5    response = self.full_dispatch_request()
 6  File "/home/abd/.local/lib/python3.9/site-packages/flask/app.py", line 1515, in full_dispatch_request
 7    rv = self.handle_user_exception(e)
 8  File "/home/abd/.local/lib/python3.9/site-packages/flask/app.py", line 1513, in full_dispatch_request
 9    rv = self.dispatch_request()
10  File "/home/abd/.local/lib/python3.9/site-packages/flask/app.py", line 1499, in dispatch_request
11    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
12  File "/home/abd/python/flask/series03/flask_app/app.py", line 8, in index
13    return render_template('index.html')
14NameError: name 'render_template' is not defined
15127.0.0.1 - - [12/Sep/2021 15:16:56] "GET / HTTP/1.1" 500 -

上面的 traceback 通过引发内部服务器错误的代码进行回复. 行 NameError: name'render_template' is not defined' 给出了问题的根源原因: render_template()' 函数没有被导入。

正如你在这里可以看到的,你必须去终端来解决错误,这并不方便。

要做到这一点,请用CTRL+C关闭服务器,并将环境变量FLASK_ENV设置为development,这样您就可以在开发模式中运行应用程序(允许调试程序),使用以下命令(在 Windows 上,使用设置而不是导出):

1export FLASK_ENV=development

运行开发服务器:

1flask run

您将在终端中看到类似的输出:

1[secondary_label Output]
2 * Serving Flask app 'app' (lazy loading)
3 * Environment: development
4 * Debug mode: on
5 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
6 * Restarting with stat
7 * Debugger is active!
8 * Debugger PIN: 120-484-907

在这里,你可以看到环境现在是开发,调试模式已启用,调试器已激活。Debugger PIN是你需要在浏览器中解锁控制台的PIN(一个互动的Python壳,你可以通过点击下面的图像中围绕的小终端图标来访问)。

刷新浏览器上的索引页面,你会看到以下页面:

The Flask Debugger

在这里,您可以看到错误消息以一种更容易理解的方式显示。第一个标题为您提供导致问题的Python例外的名称(NameError)。第二行为您提供直接的原因(render_template()未定义,这意味着在这种情况下没有导入)。

<$>[注] 注: 圆形终端图标允许您在不同的框架上在浏览器中运行Python代码。这对于当您希望在Python交互壳中检查变量的值时是有用的。当您点击终端图标时,您需要输入您在运行服务器时获得的Debugger PIN代码。您在本教程中不需要这个交互壳。

要解决这个NameError问题,请离开服务器运行,打开一个新的终端窗口,激活您的环境,并打开您的app.py文件:

1nano app.py

更改文件以显示如下:

1[label flask_app/app.py]
2
3from flask import Flask, render_template
4
5app = Flask(__name__)
6
7@app.route('/')
8def index():
9    return render_template('index.html')

保存并关闭文件。

在这里,您已导入缺少的 render_template() 函数。

随着开发服务器运行,更新您的浏览器上的索引页面。

这一次,你会看到一个错误页面,其中信息看起来像这样:

1[secondary_label Output]
2jinja2.exceptions.TemplateNotFound
3jinja2.exceptions.TemplateNotFound: index.html

此错误消息表示index.html模板不存在。

要修复此问题,您将创建一个 base.html 模板文件,以避免代码重复,然后创建一个 index.html 模板,该模板将扩展到基础模板。

创建模板目录,这是Flask搜索模板文件的目录,然后使用您最喜欢的编辑器打开一个base.html文件:

1mkdir templates
2nano templates/base.html

将以下代码添加到您的 base.html 文件中:

 1[label flask_app/templates/base.html]
 2
 3<!DOCTYPE html>
 4<html lang="en">
 5<head>
 6    <meta charset="UTF-8">
 7    <title>{% block title %} {% endblock %} - FlaskApp</title>
 8    <style>
 9        nav a {
10            color: #d64161;
11            font-size: 3em;
12            margin-left: 50px;
13            text-decoration: none;
14        }
15    </style>
16</head>
17<body>
18    <nav>
19        <a href="{{ url_for('index') }}">FlaskApp</a>
20        <a href="#">About</a>
21    </nav>
22    <hr>
23    <div class="content">
24        {% block content %} {% endblock %}
25    </div>
26</body>
27</html>

保存并关闭文件。

此基础模板包含您在其他模板中需要重复使用的所有HTML锅炉板。标题块将被更换为为每个页面设置一个标题,而内容块将被替换为每个页面的内容。导航栏有两个链接,一个用于索引页面,其中您使用url_for()辅助函数链接到index()视图函数,另一个用于关于页面,如果您选择在应用程序中包含一个。

接下来,打开一个名为index.html的模板文件,该文件将从基础模板继承。

1nano templates/index.html

添加以下代码:

1[label flask_app/templates/index.html]
2{% extends 'base.html' %}
3
4{% block content %}
5    <h1>{% block title %} Index {% endblock %}</h1>
6    <h2>Welcome to FlaskApp!</h2>
7{% endblock %}

保存并关闭文件。

在上面的代码中,您将基础模板扩展,并重置内容块,然后设置页面标题并使用标题块在H1标题中显示,并在H2标题中显示问候。

随着开发服务器运行,更新您的浏览器上的索引页面。

您将看到应用程序不再显示错误,索引页面将按预期显示。

您现在已经使用了调试模式,并看到如何处理错误消息,接下来,您将删除以您选择的错误消息响应请求,并看到如何使用自定义错误页面响应。

步骤2:创建自定义错误页面

在此步骤中,您将学习如何取消请求,并在用户请求服务器上不存在的数据时使用 404 HTTP 错误消息回复,您还将学习如何为常见的 HTTP 错误创建自定义错误页,例如404 Not Found错误和500 Internal Server Error错误。

要展示如何拒绝请求并使用自定义的 404 HTTP 错误页面响应,您将创建一个显示几个消息的页面。

首先,打开您的app.py文件,为消息页面添加新的路线:

1nano app.py

添加下列路径到文件的末尾:

1[label flask_app/app.py]
2# ...
3
4@app.route('/messages/<int:idx>')
5def message(idx):
6    messages = ['Message Zero', 'Message One', 'Message Two']
7    return render_template('message.html', message=messages[idx])

保存并关闭文件。

在上面的路径中,你有一个 URL 变量 idx. 这是指数,它会决定将显示的消息。 例如,如果 URL 是 /messages/0,则会显示第一个消息( Message Zero)。

message()视图函数中,你有一个名为messages的常规Python列表,其中包含三个消息(在现实世界的情况下,这些消息会来自数据库、API或其他外部数据源)。该函数返回了render_template()函数的呼叫,其中有两个参数,即message.html作为模板文件,以及将传送到模板的message变量。

接下来,打开一个新的「message.html」模板文件:

1nano templates/message.html

添加以下代码:

1[label flask_app/templates/message.html]
2{% extends 'base.html' %}
3
4{% block content %}
5    <h1>{% block title %} Messages {% endblock %}</h1>
6    <h2>{{ message }}</h2>
7{% endblock %}

保存并关闭文件。

在上面的代码中,您将基础模板扩展到内容块上方,在H1标题中添加一个标题(消息)并在H2标题中显示消息变量的值。

当开发服务器运行时,请访问您的浏览器上的下列URL:

1http://127.0.0.1:5000/messages/0
2http://127.0.0.1:5000/messages/1
3http://127.0.0.1:5000/messages/2
4http://127.0.0.1:5000/messages/3

您将看到H2在前三个 URL 中的每一个 URL 上都包含文本Message Zero,Message OneMessage Two。然而,在第四个 URL 上,服务器将用IndexError: list index out of range错误消息响应。

您可以使用 Flask 的 abort() 帮助函数以404 错误响应,然后打开app.py 文件:

1nano app.py

编辑第一个行以导入 abort() 函数,然后编辑 message() 视图函数,如下所示,添加一个 try... except 条款:

 1[label flask_app/app.py]
 2from flask import Flask, render_template, abort
 3
 4# ...
 5# ...
 6
 7@app.route('/messages/<int:idx>')
 8def message(idx):
 9    messages = ['Message Zero', 'Message One', 'Message Two']
10    try:
11        return render_template('message.html', message=messages[idx])
12    except IndexError:
13        abort(404)

保存并关闭文件。

在上面的代码中,您导入了 abort()函数,您使用该函数来取消请求并以错误响应。在message()视图函数中,您使用一个试用......除外条款来包装该函数。您首先尝试返回messages模板,并在URL中返回与索引相符的消息。如果该索引没有相应的消息,则将提起IndexError例外。

当开发服务器运行时,请使用您的浏览器重定向以IndexError响应的 URL(或访问任何具有大于 2 的 URL):

1http://127.0.0.1:5000/messages/3

你会看到以下答案:

1Not Found
2
3The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

您现在有一个更好的错误消息,表明服务器无法找到所请求的消息。

接下来,您将为 404 错误页和 500 错误页创建一个模板。

首先,您将使用特殊的 @app.errorhandler() 装饰器注册一个函数作为对 404 错误的处理器。

1nano app.py

通过如下方式添加突出部分来编辑文件:

 1[label flask_app/app.py]
 2from flask import Flask, render_template, abort
 3
 4app = Flask(__name__)
 5
 6@app.errorhandler(404)
 7def page_not_found(error):
 8    return render_template('404.html'), 404
 9
10@app.route('/')
11def index():
12    return render_template('index.html')
13
14@app.route('/messages/<int:idx>')
15def message(idx):
16    messages = ['Message Zero', 'Message One', 'Message Two']
17    try:
18        return render_template('message.html', message=messages[idx])
19    except IndexError:
20        abort(404)

保存并关闭文件。

在这里,您使用@app.errorhandler()编辑器将函数page_not_found()注册为自定义错误处理器。该函数将错误作为一个参数,并以称为404.html的模板返回对render_template()函数的呼叫。您将稍后创建该模板,如果您想要的话可以使用另一个名称。您还会在render_template()呼叫后返回整数404。这告诉Flask响应中的状态代码应该是404。如果您不添加它,默认状态代码响应将是`200``(LINK0),这意味着请求成功。

接下来,打开一个新的404.html模板:

1nano templates/404.html

添加以下代码:

1[label flask_app/templates/404.html]
2{% extends 'base.html' %}
3
4{% block content %}
5        <h1>{% block title %} 404 Not Found. {% endblock %}</h1>
6        <p>OOPS! Sammy couldn't find your page; looks like it doesn't exist.</p>
7        <p>If you entered the URL manually, please check your spelling and try again.</p>
8{% endblock %}

保存并关闭文件。

就像任何其他模板一样,您可以扩展基础模板,更换内容标题块的内容,并添加自己的HTML代码。

您可以在错误页面中使用您想要的任何HTML、CSS和JavaScript,就像其他模板一样。

当开发服务器运行时,请使用您的浏览器重新浏览以下URL:

1http://127.0.0.1:5000/messages/3

您将看到该页面现在有位于基板中的导航栏和自定义错误消息。

同样,您可以为您的500 内部服务器错误错误添加自定义错误页面。

1nano app.py

404错误处理器下方添加以下错误处理器:

 1[label flask_app/app.py]
 2# ...
 3
 4@app.errorhandler(404)
 5def page_not_found(error):
 6    return render_template('404.html'), 404
 7
 8@app.errorhandler(500)
 9def internal_error(error):
10    return render_template('500.html'), 500
11
12# ...

在这里,您使用与404错误处理器相同的模式。您使用app.errorhandler()装饰器与500参数,将名为internal_error()的函数转化为错误处理器。

然后,为了展示定制错误的呈现方式,在文件末尾添加一个以500 HTTP 错误响应的路径. 这个路径总是会给出一个500 内部服务器错误,无论调试程序是否正在运行:

1[label flask_app/app.py]
2
3# ...
4@app.route('/500')
5def error500():
6    abort(500)

在这里,您创建一条路线 /500,并使用 abort() 函数以响应一个 500 HTTP 错误。

保存并关闭文件。

接下来,打开新的500.html模板:

1nano templates/500.html

添加以下代码:

1[label flask_app/templates/500.html]
2{% extends 'base.html' %}
3
4{% block content %}
5        <h1>{% block title %} 500 Internal Server Error {% endblock %}</h1>
6        <p>OOOOPS! Something went wrong on the server.</p>
7        <p>Sammy is currently working on this issue. Please try again later.</p>
8{% endblock %}

保存并关闭文件。

在这里,你会做与「404.html」模板相同的事情,你会扩展基础模板,并用一个标题和两个自定义消息来替换内容块,以便告知用户内部服务器错误。

当开发服务器运行时,请访问响应500错误的路径:

1http://127.0.0.1:5000/500

您的自定义页面将显示,而不是一般错误页面。

您现在知道如何在您的 Flask 应用程序中使用自定义错误页面来处理 HTTP 错误。接下来,您将学习如何使用日志来跟踪应用程序中的事件。

步骤 3 – 使用日志来跟踪应用程序中的事件

在此步骤中,您将使用日志记录来跟踪服务器运行和应用程序使用时发生的事件,这有助于您查看应用程序代码中正在发生的事情,以便您更容易解决错误。

当开发服务器运行时,您已经看到日志,这些日志通常看起来像这样:

1127.0.0.1 - - [21/Sep/2021 14:36:45] "GET /messages/1 HTTP/1.1" 200 -
2127.0.0.1 - - [21/Sep/2021 14:36:52] "GET /messages/2 HTTP/1.1" 200 -
3127.0.0.1 - - [21/Sep/2021 14:36:54] "GET /messages/3 HTTP/1.1" 404 -

在这些日志中,您可以看到以下信息:

  • 127.0.0.1:服务器正在运行的主机
  • [21/Sep/2021 14:36:45]:请求的日期和时间
  • GET: HTTP请求方法。在这种情况下, GET用于检索数据
  • /messages/2:用户请求的路径
  • HTTP/1.1: HTTP版本
  • 200404:响应的状态代码

这些日志可以帮助您诊断应用程序中发生的问题. 您可以使用 Flask 提供的app.logger日志器来记录有关特定请求的更多信息。

通过日志,您可以使用不同的函数来报告不同日志级别的信息. 每个级别都表明某个事件发生了某种程度的严重性。

●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●

要演示如何使用 Flask 日志,请打开您的app.py文件进行编辑,以记录一些事件:

1nano app.py

编辑message()视图函数以显示如下:

 1[label flask_app/app.py]
 2
 3# ...
 4
 5@app.route('/messages/<int:idx>')
 6def message(idx):
 7    app.logger.info('Building the messages list...')
 8    messages = ['Message Zero', 'Message One', 'Message Two']
 9    try:
10        app.logger.debug('Get message with index: {}'.format(idx))
11        return render_template('message.html', message=messages[idx])
12    except IndexError:
13        app.logger.error('Index {} is causing an IndexError'.format(idx))
14        abort(404)
15
16# ...

保存并关闭文件。

在这里,您在不同级别上登录了几个事件。您使用「app.logger.info()」来登录按照预期运行的事件(即「INFO」级别)。您使用「app.logger.debug()」来获取详细信息(「DEBUG」级别),并提到应用现在正在收到具有特定索引的消息。

请访问以下URL:

1http://127.0.0.1:5000/messages/1

您将在您的服务器运行的终端中看到以下信息:

1[secondary_label Output]
2
3[2021-09-21 15:17:02,625] INFO in app: Building the messages list...
4[2021-09-21 15:17:02,626] DEBUG in app: Get message with index: 1
5127.0.0.1 - - [21/Sep/2021 15:17:02] "GET /messages/1 HTTP/1.1" 200 -

在这里,您可以看到INFO消息app.logger.info()日志,以及DEBUG消息,其中包含您使用app.logger.debug()登录的索引号。

现在,访问一个不存在的消息的URL:

1http://127.0.0.1:5000/messages/3

您将在终端中看到以下信息:

1[secondary_label Output]
2[2021-09-21 15:33:43,899] INFO in app: Building the messages list...
3[2021-09-21 15:33:43,899] DEBUG in app: Get message with index: 3
4[2021-09-21 15:33:43,900] ERROR in app: Index 3 is causing an IndexError
5127.0.0.1 - - [21/Sep/2021 15:33:43] "GET /messages/3 HTTP/1.1" 404 -

正如你所看到的,你有之前所看到的INFODEBUG日志,还有一个新的ERROR日志,因为没有一个指数为3的消息。

记录事件、详细信息和错误可以帮助您识别错误的地方,并更容易解决问题。

在此步骤中,您已经学会了如何使用 Flask 日志记录仪。 查看 如何在 Python 中使用日志记录 3 以便更好地了解日志记录。 有关日志记录的深入研究,请参阅 Flask 日志记录文件Python 日志记录文件

结论

您现在知道如何在 Flask 中使用调试模式,以及如何解决并修复一些常见的错误,您可能在开发 Flask Web 应用程序时遇到。

如果您想了解更多关于 Flask 的信息,请参阅 Flask 主题页面

Published At
Categories with 技术
comments powered by Disqus