作者选择了 自由和开源基金作为 写给捐款计划的一部分接受捐款。
介绍
Flask是一个轻量级的Python网络框架,提供用于在Python语言中创建Web应用程序的有用的工具和功能。
当您开发 Web 应用程序时,您将不可避免地遇到应用程序行为与预期相反的情况,您可能会错误编写变量,滥用为
循环,或构建一个如果
声明以一种引发Python例外的方式,例如在声明功能之前呼叫函数,或者只是寻找一个不存在的页面。
在本教程中,您将构建一个小型 Web 应用程序,展示如何处理开发 Web 应用程序时遇到的常见错误,您将创建自定义错误页面,使用 Flask 调试程序来解决异常,并使用日志来跟踪应用程序中的事件。
前提条件
- 一个本地的Python 3编程环境. 您可以在 How To Install and Set Up a Local Programming Environment for Python 3]系列中遵循您的分布的教程。 在本教程中,我们将称呼我们的项目目录
flask_app
. - 了解基本的Flask概念,如路径,视图函数和模板。 如果您不熟悉Flask,请参阅 How to Create Your First Web Application Using Flask and Python 和 How to Use Templates in a Flask Application.
- 了解基本的HTML概念。 您可以查看我们的 How To Build a Website with HTML
步骤 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
类别,然后创建一个名为app
的flask
应用程序实例. 您使用@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壳,你可以通过点击下面的图像中围绕的小终端图标来访问)。
刷新浏览器上的索引页面,你会看到以下页面:
在这里,您可以看到错误消息以一种更容易理解的方式显示。第一个标题为您提供导致问题的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 One
或Message 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版本200
或404
:响应的状态代码
这些日志可以帮助您诊断应用程序中发生的问题. 您可以使用 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 -
正如你所看到的,你有之前所看到的INFO
和DEBUG
日志,还有一个新的ERROR
日志,因为没有一个指数为3
的消息。
记录事件、详细信息和错误可以帮助您识别错误的地方,并更容易解决问题。
在此步骤中,您已经学会了如何使用 Flask 日志记录仪。 查看 如何在 Python 中使用日志记录 3 以便更好地了解日志记录。 有关日志记录的深入研究,请参阅 Flask 日志记录文件 和 Python 日志记录文件 。
结论
您现在知道如何在 Flask 中使用调试模式,以及如何解决并修复一些常见的错误,您可能在开发 Flask Web 应用程序时遇到。
如果您想了解更多关于 Flask 的信息,请参阅 Flask 主题页面。