跟着学习(新版):https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-vii-error-handling
从这章开始摒弃旧版,蛤蛤
回顾上一章:https://blog.csdn.net/weixin_41263513/article/details/85015311

本章内容

  • 自定义错误界面
  • 通过email发送错误
  • log错误日志文件
  • 修复重复用户名的bug

如果你是按照上一章一步一步往下做的,你也许不用测试也会有一个非常非常大的疑问,如果用户名在编辑的时候恰好重名了,将会发生什么呢,那当然是error啦!
\"在这里插入图片描述\"
那么怎么解决呢?可能有些小机灵鬼想到了,用户名跟邮箱分开,标明用邮箱登陆不就行了?巧了,我也是这样想的,不过我也见过不少是用用户名登陆的,所以我也就耐着性子把大大的英文给啃完了,下面的是我跟着大大的思维走的解决途径

自定义错误界面

flask为应用程序提供了一种自定义错误页面的机制,这样用户就看不到普通无聊的默认错误页面,下面让我们为HTTP错误404和500定义自定义错误页面,这是两个最常见的错误页面。 为其他错误定义页面的工作方式相同。
文件:/app/errors.py

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

错误函数与视图函数的工作方式非常相似。 对于这两个错误,我将返回各自的模板内容。 请注意,两个函数都在模板后面返回第二个值,即错误代码编号。 对于我到目前为止创建的所有视图函数,我不需要添加第二个返回值,因为默认值为200(成功响应的状态代码)是我想要的。 在这种情况下,这些是错误页面,所以我希望响应的状态代码能够反映出来。

在数据库错误之后可以调用500错误的错误处理程序,实际上是上面的用户名重复的情况。 要确保任何失败的数据库会话不会干扰到模板的运行,我会发出会话回滚。

404界面:
文件:/app/templates/404.html

{% extends \" .html\" %}

{% block content %}
    <h1>File Not Found</h1>
    <p><a href=\"{{ url_for(\'index\') }}\">Back</a></p>
{% endblock %}

505界面:
文件:/app/templates/500.html

{% extends \" .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 %}

不要忘了最重要的最后一部,把errors.py的模块导入到app初始化代码中:
文件:/app/–init–.py

# ...

from app import routes, models, errors

可能有些朋友会觉得,把上面的app.errorhandler()装饰的错误视图函数放到routes.py不行吗?
那当然是,可以啦!不过错误有很多种,不知有404和500,把这种分开来也是方便工作嘛

通过email发送错误

Flask提供的默认错误处理的另一个问题是没有通知,错误的堆栈跟踪被打印到终端,这意味着需要监视服务器进程的输出以发现错误。 当您在开发期间运行应用程序时,这非常好,但是一旦将应用程序部署在生产服务器上,就没有人会查看输出,因此需要采用更强大的解决方案。

其中一个就是将Flask配置为在发生错误后立即向我发送电子邮件,并在电子邮件正文中显示错误的堆栈跟踪。

第一步是将电子邮件服务器详细信息添加到配置文件中
文件:/config.py

class Config( ):
    # ...
    MAIL_SERVER = os.environ.get(\'MAIL_SERVER\')
    MAIL_PORT = int(os.environ.get(\'MAIL_PORT\') or 25)
    MAIL_USE_TLS = os.environ.get(\'MAIL_USE_TLS\') is not None
    MAIL_USERNAME = os.environ.get(\'MAIL_USERNAME\')
    MAIL_PASSWORD = os.environ.get(\'MAIL_PASSWORD\')
    ADMINS = [\'your-email@example.com\']

五个配置变量来自它们的环境变量对应物。 如果未在环境中设置电子邮件服务器,那么我将使用它作为需要禁用电子邮件错误的标志。 电子邮件服务器端口也可以在环境变量中给出,但如果未设置,则使用标准端口25。 默认情况下不使用电子邮件服务器凭据,但可以根据需要提供。 ADMINS配置变量是将接收错误报告的电子邮件地址列表,因此您自己的电子邮件地址应该在该列表中。

Flask使用Python的日志包来编写日志,这个包能够通过电子邮件发送日志。需要做的就是发送有关错误日志log到电子邮件
文件:/app/–init–.py

import logging
from logging.handlers import SMTPHandler

# ...

if not app.debug:
    if app.config[\'MAIL_SERVER\']:
        auth = None
        if app.config[\'MAIL_USERNAME\'] or app.config[\'MAIL_PASSWORD\']:
            auth = (app.config[\'MAIL_USERNAME\'], app.config[\'MAIL_PASSWORD\'])
        secure = None
        if app.config[\'MAIL_USE_TLS\']:
            secure = ()
        mail_handler = SMTPHandler(
            mailhost=(app.config[\'MAIL_SERVER\'], app.config[\'MAIL_PORT\']),
            fromaddr=\'no-reply@\' + app.config[\'MAIL_SERVER\'],
            toaddrs=app.config[\'ADMINS\'], subject=\'Microblog Failure\',
            credentials=auth, secure=secure)
        mail_handler.setLevel(logging.ERROR)
        app.logger.addHandler(mail_handler)

不要忘了我们00章强调过的!!!千万别在生产服务器中启用调试模式!!!
那么在debug=False的时候,就执行以上代码,创建一个SMTPHandler实例,设置其级别以便它只报告错误而不是警告,信息或调试消息,最后将它从Flask附加到app.logger对象。

关于邮件这一章大家先不要着急测试,可以先看看这篇大佬的技术展示:https://blog.csdn.net/qq_42239520/article/details/80368733
以后我再参照其他资料重新写一遍收发邮件蛤~

log错误日志文件

通过电子邮件接收错误很好,但有时这还不够。 有一些失败条件不会在Python异常中结束并且不是主要问题,但它们可能仍然足够有趣,可以保存以用于调试目的。 出于这个原因,我还要为应用程序维护一个日志文件。

要启用基于文件的日志,要启动一个处理程序(类型为RotatingFileHandler)需要以与电子邮件处理程序类似的方式附加到应用程序记录器。
文件:/app/–init–.py

# ...
from logging.handlers import RotatingFileHandler
import os

# ...

if not app.debug:
    # ...

    if not os.path.exists(\'logs\'):
        os.mkdir(\'logs\')
    file_handler = RotatingFileHandler(\'logs/microblog.log\', maxBytes=10240,
                                       backupCount=10)
    file_handler.setFormatter(logging.Formatter(
        \'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]\'))
    file_handler.setLevel(logging.INFO)
    app.logger.addHandler(file_handler)

    app.logger.setLevel(logging.INFO)
    app.logger.info(\'Microblog startup\')

把日志全填进名为microblog.log的文件,如果它不存在,就创建它。

RotatingFileHandler类可以确保在应用程序运行很长时间后日志文件不会变得太大。而且我们可以将日志文件的大小限制为10KB,并且我们会留最后十个日志文件为备份。

logging.Formatter类为日志消息提供自定义格式。由于这些消息将转到文件,我希望它们拥有尽可能多的信息。所以我使用的格式包括时间戳,日志记录级别,消息和源文件以及日志条目所在的行号。

为了使日志记录更有用,我还将日志记录级别降低到INFO类别,包括应用程序记录器和文件记录器处理程序。根据日志记录的类别,严重程度将逐渐增加,即DEBUG,INFO,WARNING,ERROR和CRITICAL。

作为日志文件的第一个有趣用途,服务器每次启动时都会在日志中写入一行。当此应用程序在生产服务器上运行时,这些日志条目将告诉您服务器何时重新启动。

修复重复用户名的bug

上面说了很多关于错误的处理,还点了点email的用法,现在开始真正的处理目前的最大boss

在注册过程中,我需要确保数据库中不存在表单中输入的用户名。 在编辑个人资料表单上,我必须执行相同的检查,但有一个例外。 如果用户保持原始用户名不变,则验证应允许,因为该用户名已分配给该用户
文件:/app/forms.py

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 Validati (\'Please use a different username.\')

上述其实跟RegistrationForm类内的validate_username方法一致呢

要使用这个新的验证方法,我需要在视图函数中添加原始用户名参数:
文件:/app/routes.py

@app.route(\'/edit_profile\', methods=[\'GET\', \'POST\'])
@login_required
def edit_profile():
    form = EditProfileForm(current_user.username)
    # ...

结束!是的,这一章最大的功绩也就解决了一个bug,但关键是,这个bug并不是什么大难题,不过因此牵出了处理error的方法,还有邮件发送的方法,关于邮件发送我也是特别感兴趣,脱离这个【跟着学习】系列后我会针对我自己的博客再重新弄一遍邮件发送的功能!这篇就没有效果图啦~蛤蛤

收藏 打印