Flask框架——应用错误处理

上篇文章我们学习Flask框架——基于类的视图,这篇文章学习Flask框架——应用错误处理。
即使我们的代码是百分百正确,但是还是会时常看见出错,这是因为和代码相关联的东西会出错,例如:

  • 客户端中断了请求,但应用程序还在读取数据;
  • 数据库已经过载,无法处理查询;
  • 文件系统没有存储空间;
  • 硬盘崩溃,后台服务过载;
  • 使用的库出现程序错误;
  • 服务器与另一个系统的网络连接出错;

除了这些错误还有很多错误,在生产环境下,我们通过把出错情况记录到日志里面。但这不是最好的处理错误方式。接下来我们学习更好的出错处理方式。

错误日志工具

当web应用程序运行发生错误时,我们可以通过日志文件来找错误信息并处理错误,但只要有足够多的用户触发了该错误或不同的错误,即使是很简单的错误,这样通过日志文件来找错误信息并处理错误就会变得很困难。

这时我们可以使用Sentry来处理应用错误,Sentry可以统计重复错误、捕获堆栈数据和本地变量用于排错,并在发生新的错误时或按指定频度发送电子邮件。

在使用Sentry时,执行如下代码安装flask依赖的sentry-sdk客户端:

pip install sentry-sdk[flask]

安装成功后,我们需要dsn值,dsn可以通过以下方式获取:

1、进入sentry注册登录网站注册账号,如下图所示:


这些注册信息可以随意填写,但Email和password需要我们记住,用来登录查看sentry发送的错误信息。

2、创建项目,如下图所示:



这里我们选择了flask,点击创建项目就会跳转到如下网页:



我们通过官网提供的示例代码来演示如何使用sentry,代码如下所示:
from flask import Flask
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
sentry_sdk.init(                            #初始化配置
    dsn="YOUR_DSN_HERE",                     #设置dsn值
    integrations=[FlaskIntegration()],        #整合器
    traces_sample_rate=1.0                   #捕获的性能数据量,值在0-1之间
)
app = Flask(__name__)

if __name__ == '__main__':
    app.run()

首先导入sentry的库和方法,在进行sentry_sdk的初始化配置,接着编写一个视图函数,代码如下所示:

@app.route('/debug-sentry')
def trigger_error():
    division_by_zero = 1 / 0

视图函数的语法没有问题,但由于0不能为被除数,所以当我们访问/debug-sentry的URL时,会触发一个错误,这个错误信息会被sentry捕获。

运行flask程序,并访问http://127.0.0.1:5000/debug-sentry,如下图所示:

该错误信息大概是说:服务器遇到内部错误,服务器过载或应用程序出错。

登录sentry后,如下图所示:


这样我们就成功捕获了错误信息和出错的次数了。

错误处理器

在Flask中发生错误时,会返回一个相应的HTTP状态码,状态码400499表示客户端的请求数据或与之相关的错误,状态码500599表示服务器或应用本身的错误。

例如:在我们请求一个不存在的URL链接时,会发出一个404 Not Found错误,如下图所示:



当发生错误时,向用户显示我们自定义的出错页面时,我们可以使用错误处理器。

错误处理器是一个函数,类似视图函数的函数,当发生某类错误时,返回一个传递了正在处理的错误的实例响应。

注册错误处理器

在使用错误处理器时,需要先注册错误处理器,注册错误处理器有两种方法:使用errorhandler装饰函数注册、使用register_error_handler()来注册 ,示例代码如下:

#使用装饰器函数注册
@app.errorhandler(werkzeug.exceptions.BadRequest)       #传递子类BadRequest
#@app.errorhandler(400)                   #或传递标准HTTP代码
def handle_bad_request(e):
    return '错误请求', 400                  #返回出错代码400
    
#使用register_error_handler()注册
def handle_bad_request(e):
    return 'bad request!', 400              #返回出错代码400
app.register_error_handler(400, handle_bad_request)

在使用errorhandler装饰器函数注册时,需要传入werkzeug.exceptions.HTTPException的子类,例如BadRequest或者传递标准HTTP代码,如400,在返回响应时需要设置出错代码。

自定义错误页面

为了更能告诉用户错误信息,我们可以自定义错误页面,首先注册一个错误处理器器,再abort()函数,该函数可以中止请求,产生HTTP错误。Flask程序示例代码如下:

import werkzeug
from flask import Flask, abort
app=Flask(__name__)

@app.errorhandler(400)
def handle_bad_request(e):
    return '错误请求', 400          #返回错误信息

@app.route('/')
def hello_world():
    abort(400)                      #使用abort()函数,传递400HTTP代码

if __name__ == '__main__':
    app.run()

启动Flask程序,访问http://127.0.0.1:5000/时,网页会显示错误请求。

除了上面返回错误信息的数据格式,我们可以将错误信息以JSON的格式来返回,示例代码如下:

from flask import Flask, abort,jsonify

app=Flask(__name__)

@app.errorhandler(404)                          #注册404错误处理器
def resource_not_found(e):
    return jsonify(error=str(e)), 404            #使用jsonify()方法接收错误对象e,并传入错误HTML代码404

@app.route('/')
def hello_world():
    abort(404,description="Resource not found")          #抛出错误处理器错误信息

if __name__ == '__main__':
    app.run(debug=True)

启动Flask程序,访问http://127.0.0.1:5000/时,网页会显示如下内容:

{
  "error": "404 Not Found: Resource not found"
}

自定义错误处理器HTTP代码

注意:Werkzeug 无法识别非标准HTTP代码,当返回出错代码不是标准的HTTP代码时,会报错,例如:返回出错代码1000

KeyError: "'1000' is not a recognized HTTP error code. Use a subclass of HTTPException with that code instead."

Werkzeug 无法识别非标准HTTP代码,也就是使用上面的方法无法注册非标准HTTP代码的错误处理器。

这时我们自定义一个HTTPException子类, 注册并抛出异常类,Flask程序示例代码如下:

import werkzeug
from flask import Flask, abort, render_template

app=Flask(__name__)

class InsufficientStorage(werkzeug.exceptions.HTTPException):       #自定义HTTP错误子类
    code=1000
    description = 'Not enough storage space.'

def handle_1000(e):
    return '服务器无法存储完成请求所必须的内容',1000                 #返回错误信息及HTTP代码
app.register_error_handler(InsufficientStorage, handle_1000)          #注册错误处理器

@app.route('/')
def hello_world():
    raise InsufficientStorage()             #抛出错误处理器错误信息

if __name__ == '__main__':
    app.run(debug=True)

这里我们创建了名为InsufficientStorage的子类,子类里面的内容可以是随意的,再创建一个名为handle_1000()的函数,该函数返回错误请求和HTTP代码。最后使用register_error_handler()方法注册错误处理器——InsufficientStorage子类和handle_507。

启动Flask程序,访问http://127.0.0.1:5000/,如下图所示:

通用错误处理器

通用错误处理器就是把HTTP出错信息转换为JSON并展示在网页中,示例代码如下所示:

import werkzeug
from flask import Flask, abort, render_template
from flask import json
from werkzeug.exceptions import HTTPException

app=Flask(__name__)

@app.errorhandler(HTTPException)                #注册错误处理器
def handle_exception(e):                       #错误处理器函数传入错误对象e
    response = e.get_response()                 #获取错误响应
    response.data = json.dumps({                #编辑错误响应内容
        "code": e.code,
        "name": e.name,
        "description": e.description,
    })
    response.content_type = "application/json"   #设置响应类型
    return response

@app.route('/')
def hello_world():
    abort(501)                              #使用abort()传入501错误代码

if __name__ == '__main__':
    app.run(debug=True)

首先使用errorhandler装饰器注册错误处理器,并定义错误处理器函数接收错误对象e,根据错误对象e来编辑返回错误响应内容。

启动Flask程序,访问http://127.0.0.1:5000/,如下图所示:

好了,Flask框架——应用错误处理就学到这里了,感谢观看,下篇文章我们继续学习Flask框架——Bootstrap-Flask使用。

公众号:白巧克力LIN

该公众号发布Python、数据库、Linux、Flask、自动化测试、Git等相关文章!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容