網站首頁 編程語言 正文
flask響應錯誤處理及errorhandler應用
@app.errorhandler(404)
def page_not_found(error):
return render_template('404.html'),404
則當互出現請求錯誤時,并不一定需要設置重定向路由,僅定義一個errorhandler對應的錯誤頁面即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>404</h1>
<h1>很抱歉!</h1>
<p>您訪問的頁面不存在</p>
</body>
</html>!
flask學習筆記:錯誤處理
1. 做好準備工作
- 進入項目主目錄
- 激活虛擬環境
2. Flask中的錯誤處理
登陸賬號,點開編輯資料頁面,試著將用戶名改為一個已經存在的用戶名,然后,你會看到屏幕顯示“Internal Server Error”。
現在,看看命令行終端,你能看到錯誤堆棧跟蹤,堆棧跟蹤在錯誤調試時非常有用,因為它們顯示該堆棧中的調用序列,一直到產生錯誤的那行:
(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
堆棧跟蹤指出bug出在哪,應用允許用戶更改用戶名,但是并不會驗證用戶選擇的新用戶名是否與系統中已有的用戶發生沖突。該錯誤來自SQLAlchemy,它試圖將新用戶名寫入數據庫,但由于用戶名字段設置了 unique=True,所以寫入被拒絕。
現在默認的錯誤頁面很丑,與整個應用的布局不符。
3. 調試模式
在生產服務器中,上面那樣的處理錯誤的方式很好,如果出現了錯誤,用戶將看到一個籠統的錯誤頁面,而詳細的錯誤詳細輸出到服務器進程或者日志文件中。
但當你在開發應用時,你可以開啟調試模式,Flask會在瀏覽器中輸出非常好的調試器。先關閉應用,設置以下環境變量即可激活調試模式:
(venv) $ export FLASK_DEBUG=1
Windows中使用 set 來設置。
設置完 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表達式,例如檢查變量的值。
調試器允許用戶遠程執行服務器中的代碼,因此對于想要滲入應用的而已用戶來說有利。作為附加的安全措施,在瀏覽器中允許的調試器剛開始是鎖定的,在第一次使用時,會要求輸入PIN碼,你可以在flask run命令的輸出中看到。
另外,調試模式還有個很重要的特性,就是重新載入(reloader),這是個很有用的開發特性,如果你在開啟調試模式時運行 flask run,修改了源文件并保存,應用就會自動重新載入。
4. 自定義錯誤頁面
Flask提供了一種機制使應用可以安裝自己的錯誤頁面,而不是默認的無聊頁面。比如,我們來定義404錯誤頁面和500錯誤頁面,這兩種錯誤是最常見的。定義其他錯誤頁面同理。
用 @errorhandler 裝飾器可以自定義錯誤管理器。新建 app/errors.py 模塊。
app/routes.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 "base.html" %}
?
{% block content %}
? ? <h1>File Not Found</h1>
? ? <p><a href="{{ url_for('index') }}" >Back</a></p>
{% endblock %}
這是500錯誤的:
app/templates/500.html
{% 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 %}
兩個模板都繼承自 base.html 模板。
在 app/errors.py 模塊導入到 __init__.py 文件中應用實例的后面。
app/__init__.py
# ...
?
from app import routes, models, errors
在終端設置 FLASK_DEBUG=0 ,然后試著再次觸發用戶名重名錯誤,你就能看到更友好的錯誤頁面了。
5. 通過郵件發送錯誤
Flask提供的錯誤處理機制有個問題,就是沒有提示,只會在終端輸出堆棧跟蹤,開發時倒是沒事,但如果應用部署到了生產服務器,沒人會盯著輸出看,所以要想個別的辦法。
第一種解決方案是觸發錯誤后發郵件通知管理員,郵件正文包含堆棧跟蹤。
第一步,將郵件服務信息添加到配置文件:
config.py
class Config(object):
? ? # ...
? ? 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端口。ADMIN 配置變量代表用于接收錯誤報告的郵箱地址。
Flask用Python的 logging 包來寫入日志,這個包可用于用郵件發送日志。我要做的就是添加 SMTPHandler 實例到Flask記錄器對象,也就是 app.logger:
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)
以上程序只在調試模式為開啟時才會開啟郵件記錄器功能,也就是當 app.debug 為 True 以及郵件服務可用時。
設置郵件記錄器有些繁瑣,因為必須要處理許多郵件服務的安全可選項。但實際上,上面的代碼創建了 SMTPHandler 實例。設置其級別,使其只報告錯誤信息。
測試此功能的方法有兩種。簡單的這種是使用 Python 中的 SMTP 調試服務,接收郵件而不是發送郵件,并將其打印大盤控制臺。再打開一個終端會話并運行以下命令:
(venv) $ python -m smtpd -n -c DebuggingServer localhost:8025
設置 MAIL_SERVER=localhost 和 MAIL_PORT=8025。如果用的是 Linux 或者 Mac OS 系統,命令前要加上 sudo 前綴。如果用的是 Windows 系統,要以管理員權限打開終端。此命令需要管理員權限,1024端口以下是是只有管理員才能使用的端口。或者你也可以改為更高的端口號,比如5025,并將 MAIL_PORT 變量設置為環境中選擇的端口,這樣就不需要管理員權限了。
第二個終端的SMTP調試服務保持運行,在第一個終端環境中設置 export MAIL_SERVER=localhost 和 MAIL_PORT=8025(Windows中用set)。由于在調試模式中應用不會發送郵件,所以將FLASK_DEBUG 變量設置為 0 或者不用設置。運行應用,再觸發一次 SQLAlchemy 錯誤,在終端就能看到含有堆棧錯誤的郵件了。
第二種測試方式是配置真正的郵件服務。下面是如何使用 Gmail 的郵件服務:
export MAIL_SERVER=smtp.googlemail.com
export MAIL_PORT=587
export MAIL_USE_TLS=1
export MAIL_USERNAME=<your-gmail-username>
export MAIL_PASSWORD=<your-gmail-password>
在 Windows 系統種用 set 而不是 export 。
6. 記錄到文件
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 的日志文件寫入 logs 文件夾。
RotatingFileHandler 類確保日志文件不會太大。在本例中我將日志文件的大小限制為 10KB,并且將最后10個日志文件保留為備份。
logging.Formatter 類為日志消息提供自定義格式。由于這些消息會被記錄到文件,所以格式中包含時間戳,日志級別,消息,日志項起始位置的源文件和行號。
我將應用記錄器和文件記錄器處理器的日志等級都設置為 INFO。(日志級別按照嚴重程度分別是:DEBUG,INFO,WARNING,ERROR,CRITICAL)。
每次服務器啟動時都會往日志寫入行,當這個應用在生產服務器中運行時,能看出服務器上面時候重啟。
7. ?修復用戶名重名漏洞
RegistrationForm 已經實現了用戶名驗證,但編輯表單并沒有。
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 ValidationError('Please use a different username.')
通過自定義驗證方法可以實現這個功能,但這里還有個重載構造函數,它接受原始用戶名作為參數。此用戶名被保存為實例變量,然后用validate_username()方法檢查。如果表單中的用戶名與原始用戶名相同,就不需要檢查數據庫中環有沒有重復項了。
將原始用戶名作為參數加入到視圖函數。
app/routes.py
@app.route('/edit_profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
? ? form = EditProfileForm(current_user.username)
? ? # ...
總結
原文鏈接:https://blog.csdn.net/rytyy/article/details/78947045
相關推薦
- 2023-12-10 記錄一次導出Excel報表的錯誤
- 2022-04-28 Python可視化學習之seaborn調色盤_python
- 2024-07-15 SpringBoot使用Apache Poi導出word文檔
- 2023-03-03 C語言malloc與calloc區別詳解_C 語言
- 2022-08-08 pandas?給dataframe添加列名的兩種方法_python
- 2022-10-01 Python?from?import導包ModuleNotFoundError?No?module?
- 2021-12-13 C語言數據結構與算法之圖的遍歷(一)_C 語言
- 2022-10-14 linux【centos 7】 yum 安裝 tesseract 4.1
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支