如果你在运行应用程序的终端会话中查看,则会看到该错误的堆栈跟踪。 堆栈跟踪对于调试错误非常有用,因为它们显示了该堆栈中的调用顺序,一直到产生错误的行:
venv) $ flask run
* Serving Flask app "microblog"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
[2017-09-14 22:40:02,027] ERROR in app: Exception on /edit_profile [POST]
Traceback (most recent call last):
File "/home/miguel/microblog/venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1182, in _execute_context
context)
File "/home/miguel/microblog/venv/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 470, in do_execute
cursor.execute(statement, parameters)
sqlite3.IntegrityError: UNIQUE constraint failed: user.username
Debug Mode
你看到上面处理错误的方式非常适合在生产服务器上运行的系统。 如果出现错误,用户将获得一个模糊的错误页面(尽管我将使该错误页面更好),并且该错误的重要细节在服务器进程输出或日志文件中。
但是,在开发应用程序时,可以启用调试模式,Flask在该模式下直接在浏览器上输出一个非常好的调试器。 要激活调试模式,请停止应用程序,然后设置以下环境变量:
(venv) $ export FLASK_DEBUG=1
如果你使用的是Microsoft Windows,请记住使用set
而不是export
。
设置FLASK_DEBUG
后,重新启动服务器。 终端上的输出将与你所看到的有所不同:
(venv) microblog2 $ flask run
* Serving Flask app "microblog"
* Forcing debug mode on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 177-562-960
调试器允许你扩展每个堆栈框架并查看相应的源代码。你还可以在任何框架上打开Python提示符并执行任何有效的Python表达式,例如,检查变量的值。
绝对不要在生产服务器上以调试模式运行Flask应用程序,这一点非常重要。调试器允许用户远程执行服务器中的代码,因此对于想要渗透到你的应用程序或服务器中的恶意用户来说,这可能是意外的礼物。作为一项附加的安全措施,在浏览器中运行的调试器将开始锁定,并且在首次使用时会询问PIN码,你可以在flask run
命令的输出中看到该PIN码。
由于我处于调试模式的主题,因此我应该提到调试模式启用的第二个重要功能,即重新加载器(reloader)。这是一项非常有用的开发功能,在修改源文件后会自动重新启动应用程序。如果在调试模式下运行flask run
,则可以在应用程序上工作,并且每次保存文件时,应用程序都会重新启动以获取新代码。
Custom Error Pages
Flask为应用程序提供了一种机制来安装其自己的错误页面,从而使你的用户不必看到无聊的默认页面。 例如,让我们为最常见的两个HTTP错误404和500定义自定义错误页面。 定义页面以查找其他错误的工作方式相同。
要声明自定义错误处理程序,请使用@errorhandler
装饰器。 我将把错误处理程序放入新的app/errors.py
模块中。
# app/errors.py: Custom error handlers
from flask import render_template
from app import app, db
@app.errorhandler(404)
def not_found_error(error):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_error(error):
db.session.rollback()
return render_template('500.html'), 500
这是404错误的模板:
<!-- app/templates/404.html: Not found error template -->
{% extends "base.html" %}
{% block content %}
<h1>File Not Found</h1>
<p><a href="{{ url_for('index') }}">Back</a></p>
{% endblock %}
这是500错误的模板:
<!-- app/templates/500.html: Internal server error template -->
{% extends "base.html" %}
{% block content %}
<h1>An unexpected error has occurred</h1>
<p>The administrator has been notified. Sorry for the inconvenience!</p>
<p><a href="{{ url_for('index') }}">Back</a></p>
{% endblock %}
我利用用户名重复的错误的时间太久。 我已经向你展示了如何在应用程序中处理此类错误,现在我可以继续修复它了。
如果你还记得,RegistrationForm
已经实现了用户名的验证,但是编辑表单的要求略有不同。 注册期间,我需要确保在表单中输入的用户名在数据库中不存在。 在Edit Profile表单上,我必须执行相同的检查,但有一个例外。 如果用户保持原始用户名不变,则验证应允许该用户名,因为该用户名已分配给该用户。 在下面,你可以看到我如何实现此表单的用户名验证:
# app/forms.py: Validate username in edit profile form.
class EditProfileForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
about_me = TextAreaField('About me', validators=[Length(min=0, max=140)])
submit = SubmitField('Submit')
def __init__(self, original_username, *args, **kwargs):
super(EditProfileForm, self).__init__(*args, **kwargs)
self.original_username = original_username
def validate_username(self, username):
if username.data != self.original_username:
user = User.query.filter_by(username=self.username.data).first()
if user is not None:
raise ValidationError('Please use a different username.')