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

您所在的位置:网站首页 调试错误怎么解决问题 如何处理Flask应用程序中的错误

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

2023-07-23 10:24| 来源: 网络整理| 查看: 265

作者选择了自由与开源基金,作为Write for DOnations计划的一部分接受捐赠。

简介

Flask是一个轻量级的Python网络框架,为用Python语言创建网络应用提供了有用的工具和功能。

当你在开发一个Web应用程序时,你将不可避免地遇到这样的情况:你的应用程序的行为方式与你的预期相反。你可能拼错了一个变量,误用了一个for 循环,或者以引发Python异常的方式构造一个if 语句,比如在声明一个函数之前调用它,或者简单地寻找一个不存在的页面。如果你学会了如何正确地处理错误和异常,你会发现开发你的Flask应用程序会更容易、更顺利。

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

前提条件

一个本地Python 3编程环境。你可以按照How To Install and Set Up a Local Programming Environment for Python 3系列中你的发行版的教程来做。在本教程中,我们将我们的项目目录称为flask_app 。

对Flask基本概念的理解,如路由、视图函数和模板。如果你不熟悉Flask,请查看《如何使用Flask和Python创建你的第一个Web应用程序》和《如何在Flask应用程序中使用模板》。

对基本HTML概念的理解。你可以查看我们的《如何用HTML建立网站》系列教程,了解背景知识。

第1步 - 使用Flask调试器

在这一步,你将创建一个有一些错误的应用程序,并在没有调试模式的情况下运行它,看看应用程序的反应。然后,你将在调试模式下运行它,并使用调试器对应用程序的错误进行故障排除。

激活你的编程环境并安装Flask,在你的flask_app 目录中打开一个名为app.py 的文件进行编辑。

nano app.py 复制代码

在app.py 文件内添加以下代码。

flask_app/app.py

from flask import Flask app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') 复制代码

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

保存并关闭该文件。

接下来,使用下面的命令,用FLASK_APP 环境变量通知Flask这个应用程序(在Windows下,使用set ,而不是export )。

export FLASK_APP=app 复制代码

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

flask run 复制代码

你会在终端看到以下信息。

Output * Serving Flask app 'app' (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 复制代码

该输出提供了以下信息。

被服务的Flask应用程序(本例中为app.py )

环境,这里是production 。警告信息强调,这个服务器不是用于生产部署的。你正在使用这个服务器进行开发,所以你可以忽略这个警告,但要了解更多信息,请看Flask文档中的部署选项页面。你也可以看看这个使用Gunicorn的Flask部署教程,或者这个使用uWSGI的教程,或者你可以使用DigitalOcean App Platform来部署你的Flask应用,按照如何使用Gunicorn部署Flask应用到App Platform教程。

调试模式是关闭的,这意味着Flask调试器没有运行,你将不会在你的应用程序中收到有用的错误信息。在生产环境中,显示详细的错误会使你的应用程序暴露在安全漏洞中。

服务器正在http://127.0.0.1:5000/ URL上运行。要停止服务器,请使用CTRL+C ,但现在还不要这么做。

现在,用你的浏览器访问索引页。

http://127.0.0.1:5000/ 复制代码

你会看到一个看起来像下面的信息。

OutputInternal Server Error The 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内部服务器错误,这是一个服务器错误响应,表明服务器在应用代码中遇到了一个内部错误。

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

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

上面的回溯经过了触发内部服务器错误的代码。NameError: name 'render_template' is not defined 这一行给出了问题的根本原因:render_template() 函数没有被导入。

正如你在这里看到的,你必须到终端去排查错误,这并不方便。

你可以通过在你的开发服务器中启用调试模式来获得更好的故障排除体验。要做到这一点,用CTRL+C 停止服务器,并将环境变量FLASK_ENV 设置为development ,这样你就可以在开发模式下运行应用程序(启用调试器),使用下面的命令(在Windows下,使用set ,而不是export )。

export FLASK_ENV=development 复制代码

运行开发服务器。

flask run 复制代码

你会在终端看到与下面类似的输出。

Output * Serving Flask app 'app' (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 120-484-907 复制代码

在这里你可以看到现在的环境是development ,调试模式是打开的,而且调试器是激活的。Debugger PIN 是你在浏览器中解锁控制台所需的密码(你可以通过点击下图中包围着的小终端图标进入一个交互式的python shell)。

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

The Flask Debugger

在这里,你看到错误信息以一种更容易理解的方式显示。第一个标题给了你导致问题的Python异常的名字 (NameError在这个例子中)。)第二行给了你直接的原因 (render_template() 没有被定义,这意味着在这个例子中它没有被导入)。在这之后,你可以看到执行的Flask内部代码的回溯。从下往上看回溯,因为回溯中的最后一行通常有最有用的信息。

注意。 被圈住的终端图标允许你在浏览器的不同框架上运行Python代码。当你想像在Python交互式外壳中那样检查一个变量的值时,这很有用。当你点击终端图标时,你需要键入你在运行服务器时得到的调试器PIN码。在本教程中你将不需要这个交互式 shell。

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

nano app.py 复制代码

修改该文件,使其看起来如下。

flask_app/app.py

from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') 复制代码

保存并关闭该文件。

这里你导入了缺少的render_template() 函数。

在开发服务器运行时,刷新浏览器上的索引页。

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

Outputjinja2.exceptions.TemplateNotFound jinja2.exceptions.TemplateNotFound: index.html 复制代码

这个错误信息表明,index.html 模板不存在。

为了解决这个问题,你将创建一个base.html 模板文件,其他模板将从该文件继承,以避免代码重复,然后创建一个index.html 模板,扩展基础模板。

创建templates 目录,这是Flask寻找模板文件的目录。然后用你喜欢的编辑器打开一个base.html 文件。

mkdir templates nano templates/base.html 复制代码

在你的base.html 文件中添加以下代码。

flask_app/templates/base.html

{% block title %} {% endblock %} - FlaskApp nav a { color: #d64161; font-size: 3em; margin-left: 50px; text-decoration: none; } FlaskApp About {% block content %} {% endblock %} 复制代码

保存并关闭该文件。

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

接下来,打开一个名为index.html 的模板文件,它将继承自基本模板。

nano templates/index.html 复制代码

添加以下代码。

flask_app/templates/index.html

{% extends 'base.html' %} {% block content %} {% block title %} Index {% endblock %} Welcome to FlaskApp! {% endblock %} 复制代码

保存并关闭该文件。

在上面的代码中,你扩展了基本模板并覆盖了content 块。然后你设置了一个页面标题,并使用title 块将其显示在H1 的标题中,并在H2 的标题中显示问候语。

在开发服务器运行的情况下,刷新你的浏览器上的索引页。

你会看到应用程序不再显示错误,而且索引页也按预期显示。

你现在已经使用了调试模式,并且看到了如何处理错误信息。接下来,你将中止一个请求,用你选择的错误信息来回应,并看看如何用自定义错误页面来回应。

第2步 - 创建自定义错误页面

在这一步,你将学习如何中止请求,当用户请求的数据在服务器上不存在时,用404 HTTP错误信息来回应。你还将学习如何为常见的HTTP错误创建自定义错误页面,如404 Not Found 错误,以及500 Internal Server Error 错误。

为了演示如何中止请求并以自定义404 HTTP错误页面进行响应,你将创建一个显示一些信息的页面。如果请求的信息不存在,你将用一个404错误来回应。

首先,打开你的app.py 文件,为信息页面添加一个新的路由。

nano app.py 复制代码

在文件的最后添加以下路由。

flask_app/app.py

# ... @app.route('/messages/') def message(idx): messages = ['Message Zero', 'Message One', 'Message Two'] return render_template('message.html', message=messages[idx]) 复制代码

保存并关闭该文件。

在上面的路由中,你有一个URL变量idx 。这是决定将显示什么信息的索引。例如,如果URL是 /messages/0,第一条信息 (Message Zero) 将被显示。你使用int 转换器,只接受正整数,因为URL变量默认为字符串值。

在message() 视图函数中,你有一个名为messages 的常规 Python 列表,其中有三条消息。(在现实世界中,这些消息将来自数据库、API 或其他外部数据源)。该函数返回对render_template() 函数的调用,有两个参数,message.html 作为模板文件,和一个message 变量,它将被传递给模板。这个变量将有一个来自messages 列表的项目,取决于URL中idx 变量的值。

接下来打开一个新的message.html 模板文件。

nano templates/message.html 复制代码

在其中添加以下代码。

flask_app/templates/message.html

{% extends 'base.html' %} {% block content %} {% block title %} Messages {% endblock %} {{ message }} {% endblock %} 复制代码

保存并关闭该文件。

在上面的代码中,你扩展了基本模板并覆盖了content 块。你在H1标题中添加了一个标题(Messages),并在H2标题中显示message 的变量值。

在开发服务器运行的情况下,在你的浏览器上访问以下URL。

http://127.0.0.1:5000/messages/0 http://127.0.0.1:5000/messages/1 http://127.0.0.1:5000/messages/2 http://127.0.0.1:5000/messages/3 复制代码

你会看到H2 ,在前三个URL中的每一个都包含文本Message Zero 、Message One 、或Message Two 。然而,在第四个URL上,服务器将回应一个IndexError: list index out of range 的错误信息。在生产环境中,响应应该是500 Internal Server Error ,但这里正确的响应是404 Not Found ,表明服务器找不到索引为3 的信息。

你可以使用Flask的abort() 辅助函数来回应一个404 错误。要做到这一点,请打开app.py 文件。

nano app.py 复制代码

编辑第一行,导入abort() 函数。然后编辑message() 视图函数,添加一个try ... except 子句,如下面高亮部分所示。

flask_app/app.py

from flask import Flask, render_template, abort # ... # ... @app.route('/messages/') def message(idx): messages = ['Message Zero', 'Message One', 'Message Two'] try: return render_template('message.html', message=messages[idx]) except IndexError: abort(404) 复制代码

保存并关闭该文件。

在上面的代码中,你导入了 abort()函数,你用它来中止请求并回应一个错误。在message() 视图函数中,你使用一个try ... except 子句来包装该函数。你首先尝试返回messages 模板,其中的信息与URL中的索引相对应。如果索引没有对应的消息,将引发IndexError 异常。然后,你使用except 子句来捕捉这个错误,并使用 abort(404)来中止请求,并回应一个404 Not Found HTTP错误。

在开发服务器运行的情况下,使用你的浏览器重新访问之前响应IndexError 的URL(或者访问任何索引大于2的URL)。

http://127.0.0.1:5000/messages/3 复制代码

你会看到下面的响应。

Not Found The 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 错误的处理程序。打开app.py 文件进行编辑。

nano app.py 复制代码

编辑该文件,添加高亮部分,如下所示。

flask_app/app.py

from flask import Flask, render_template, abort app = Flask(__name__) @app.errorhandler(404) def page_not_found(error): return render_template('404.html'), 404 @app.route('/') def index(): return render_template('index.html') @app.route('/messages/') def message(idx): messages = ['Message Zero', 'Message One', 'Message Two'] try: return render_template('message.html', message=messages[idx]) except IndexError: abort(404) 复制代码

保存并关闭该文件。

在这里,你使用@app.errorhandler() 装饰器来注册函数page_not_found() ,作为一个自定义的错误处理程序。该函数将错误作为一个参数,并返回对render_template() 函数的调用,其模板名为404.html 。你将在后面创建这个模板,如果你想的话,你可以使用另一个名字。在调用render_template() 后,你还会返回整数404 。这告诉Flask,响应中的状态码应该是404 。如果你不添加它,默认的状态码响应将是 200,这意味着请求已经成功了。

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

nano templates/404.html 复制代码

将以下代码添加到其中。

flask_app/templates/404.html

{% extends 'base.html' %} {% block content %} {% block title %} 404 Not Found. {% endblock %} OOPS! Sammy couldn't find your page; looks like it doesn't exist. If you entered the URL manually, please check your spelling and try again. {% endblock %} 复制代码

保存并关闭该文件。

就像其他模板一样,你扩展基础模板,替换content 和title 块的内容,并添加你自己的HTML代码。在这里,你有一个 作为标题,一个

标签,上面有一个自定义的错误信息,告诉用户没有找到这个页面,还有一个对可能手动输入URL的用户的帮助信息。

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

在开发服务器运行的情况下,用你的浏览器重新访问以下URL。

http://127.0.0.1:5000/messages/3 复制代码

你会看到该页面现在有基本模板中的导航栏和自定义错误信息。

同样地,你可以为你的500 Internal Server Error 错误添加一个自定义错误页面。打开app.py 文件。

nano app.py 复制代码

在404 的错误处理程序下面添加以下错误处理程序。

flask_app/app.py

# ... @app.errorhandler(404) def page_not_found(error): return render_template('404.html'), 404 @app.errorhandler(500) def internal_error(error): return render_template('500.html'), 500 # ... 复制代码

这里你使用的模式与你在404 错误处理程序中使用的相同。你使用带有500 参数的app.errorhandler() 装饰器,使一个名为internal_error() 的函数成为一个错误处理程序。你渲染一个叫做500.html 的模板,并以一个状态代码500 来回应。

然后,为了演示自定义错误将如何呈现,在文件末尾添加一个响应为500 HTTP错误的路由。无论调试器是否在运行,这个路由都会给出一个500 Internal Server Error 。

flask_app/app.py

# ... @app.route('/500') def error500(): abort(500) 复制代码

这里你做了一个路由/500 ,并使用abort() 函数来响应一个500 HTTP错误。

保存并关闭该文件。

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

nano templates/500.html 复制代码

添加以下代码。

flask_app/templates/500.html

{% extends 'base.html' %} {% block content %} {% block title %} 500 Internal Server Error {% endblock %} OOOOPS! Something went wrong on the server. Sammy is currently working on this issue. Please try again later. {% endblock %} 复制代码

保存并关闭该文件。

在这里,你做了和404.html 模板一样的事情。你扩展了基本模板,并将内容块替换为标题和两条自定义信息,告知用户内部服务器错误。

在开发服务器运行的情况下,访问反应为500 错误的路由。

http://127.0.0.1:5000/500 复制代码

你的自定义页面将出现,而不是一般的错误页面。

你现在知道如何在你的Flask应用程序中使用自定义错误页面处理HTTP错误了。接下来,你将学习如何使用日志来跟踪应用程序中的事件。追踪事件有助于你了解你的代码是如何表现的,这反过来又有助于开发和故障排除。

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

在这一步中,你将使用日志来跟踪服务器运行和应用程序使用时发生的事件,这有助于你看到你的应用程序代码中发生了什么,这样你就可以更容易地排除错误。

你已经看到了每当开发服务器运行时的日志,它通常看起来像这样。

127.0.0.1 - - [21/Sep/2021 14:36:45] "GET /messages/1 HTTP/1.1" 200 - 127.0.0.1 - - [21/Sep/2021 14:36:52] "GET /messages/2 HTTP/1.1" 200 - 127.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 ,记录更多信息。

通过日志,你可以使用不同的函数来报告不同日志级别的信息。每个级别都表示一个事件的发生具有一定的严重性。可以使用以下函数。

app.logger.debug(): 对于事件的详细信息。 app.logger.info(): 确认事情正按预期进行。 app.logger.warning() :表明发生了一些意外的事情(如 "磁盘空间低"),但应用程序正在按预期工作。 app.logger.error(): 在应用程序的某些部分发生了错误。 app.logger.critical(): 一个关键的错误;整个应用程序可能会停止工作。

为了演示如何使用Flask记录器,打开你的app.py 文件进行编辑,记录一些事件。

nano app.py 复制代码

编辑message() 视图函数,看起来如下。

flask_app/app.py

# ... @app.route('/messages/') def message(idx): app.logger.info('Building the messages list...') messages = ['Message Zero', 'Message One', 'Message Two'] try: app.logger.debug('Get message with index: {}'.format(idx)) return render_template('message.html', message=messages[idx]) except IndexError: app.logger.error('Index {} is causing an IndexError'.format(idx)) abort(404) # ... 复制代码

保存并关闭该文件。

在这里,你在不同层次上记录了几个事件。你用app.logger.info() 来记录一个按预期工作的事件(这是一个INFO 的级别)。你用app.logger.debug() 来记录详细的信息(DEBUG 级别),提到应用程序现在得到了一个具有特定索引的消息。然后你用app.logger.error() 来记录这样一个事实:一个IndexError 异常已经被引发了,而且是导致问题的特定索引(ERROR 级,因为发生了一个错误)。

访问下面的URL。

http://127.0.0.1:5000/messages/1 复制代码

在你的服务器运行的终端中,你会看到以下信息。

Output [2021-09-21 15:17:02,625] INFO in app: Building the messages list... [2021-09-21 15:17:02,626] DEBUG in app: Get message with index: 1 127.0.0.1 - - [21/Sep/2021 15:17:02] "GET /messages/1 HTTP/1.1" 200 - 复制代码

在这里,你看到INFO 消息app.logger.info() 记录,以及你用app.logger.debug() 记录的索引号的DEBUG 消息。

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

http://127.0.0.1:5000/messages/3 复制代码

你会在终端看到以下信息。

Output[2021-09-21 15:33:43,899] INFO in app: Building the messages list... [2021-09-21 15:33:43,899] DEBUG in app: Get message with index: 3 [2021-09-21 15:33:43,900] ERROR in app: Index 3 is causing an IndexError 127.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应用时可能遇到的一些常见错误。你还为常见的HTTP错误创建了自定义错误页面,并使用Flask日志记录器来跟踪应用程序中的事件,以帮助你检查和弄清你的应用程序的行为方式。

如果你想阅读更多关于Flask的信息,请查看Flask主题页面。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3