
本文旨在解决 flask-security-too 中 `send_mail_task` 装饰器弃用后异步邮件发送的重构问题。我们将介绍如何通过自定义基于 `threading` 模块的装饰器,实现任意函数的异步执行,并将其应用于 flask 应用中的邮件发送功能,从而确保用户体验流畅,同时避免阻塞主应用进程。此方案提供了一种灵活且通用的异步处理机制。
在开发 Flask 应用程序时,用户认证和邮件通知是常见的功能组合。Flask-Security-Too 是一个强大的认证扩展,它曾提供 @security.send_mail_task 装饰器,用于将邮件发送操作异步化,以避免阻塞主请求线程,从而提升用户体验。然而,随着 Flask-Security-Too 版本的更新,该装饰器已被弃用。这意味着开发者需要寻找新的策略来处理异步邮件发送,特别是在需要发送如注册确认、密码重置等安全相关邮件时。
原有的实现方式可能如下所示,其中 delay_security_email 函数通过 @security.send_mail_task 装饰器实现了异步调用:
from flask_mail import Mail
from flask import Flask
mail = Mail()
# security = None # 假设 security 对象已在其他地方初始化
def create_app(test_config=None):
app = Flask(__name__)
# ... 配置 app 和 Flask-Security-Too ...
mail.init_app(app)
# global security # 示例中假设 security 是全局的或已传入
# security = Security(app, user_datastore) # 示例初始化
# 已弃用的装饰器用法示例
# @security.send_mail_task
# def delay_security_email(msg):
# with app.app_context():
# send_security_email(msg)
def send_security_email(msg):
# 使用 Flask-Mail 扩展实例发送传入的 `msg` 参数
with app.app_context():
mail.send(msg)
return app当 @security.send_mail_task 不再可用时,直接调用 send_security_email(msg) 将会同步执行,阻塞当前请求。因此,我们需要一种通用的方法来实现函数的异步执行。
为了替代被弃用的功能,我们可以设计一个自定义的 Python 装饰器,利用 threading 模块在单独的线程中执行目标函数。这种方法不仅适用于邮件发送,还可以应用于任何需要异步执行而不阻塞主线程的任务。
以下是 async_action 装饰器的实现:
import threading
from functools import wraps
def async_action(fn):
"""
一个通用装饰器,用于在单独的线程中异步执行函数。
"""
@wraps(fn)
def wrapped(*args, **kwargs):
# 创建一个新线程来执行目标函数
thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
thread.start() # 启动线程
return wrapped代码解析:
有了 async_action 装饰器,我们现在可以轻松地将它应用到 Flask-Mail 的邮件发送函数上。
from flask import Flask
from flask_mail import Mail, Message
# from flask_security_too import Security, SQLAlchemySessionUserDatastore # 示例导入
import threading
from functools import wraps
# 自定义的异步装饰器
def async_action(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
thread.start()
return wrapped
# 邮件发送函数,现在使用我们自定义的异步装饰器
@async_action
def send_security_email_async(app_context, msg):
"""
异步发送安全相关邮件。
需要传入 app_context 以便在新线程中正确获取 Flask 应用上下文。
"""
with app_context:
mail.send(msg)
# Flask-Mail 实例(全局或通过 create_app 初始化)
mail = Mail()
def create_app():
app = Flask(__name__)
app.config.update(
SECRET_KEY='a_very_secret_key', # 用于 Flask-Security-Too 或其他安全功能
MAIL_SERVER='smtp.example.com',
MAIL_PORT=587,
MAIL_USE_TLS=True,
MAIL_USERNAME='your_email@example.com',
MAIL_PASSWORD='your_password',
MAIL_DEFAULT_SENDER='your_email@example.com'
)
# ... 其他 Flask 和 Flask-Security-Too 配置 ...
mail.init_app(app)
# security.init_app(app, user_datastore) # 示例初始化
@app.route('/')
def index():
return "Hello, Flask App! Visit /send_test_email to send an async email."
@app.route('/send_test_email')
def send_test_email():
msg = Message("Hello from Flask",
sender="your_email@example.com",
recipients=["recipient@example.com"])
msg.body = "This is a test email sent asynchronously using a custom decorator."
# 在主线程中获取应用上下文,并传递给异步函数
# app.app_context() 返回一个上下文管理器,我们需要获取其内部的 app_context 栈
# 或者直接传递 app 实例,让异步函数自行创建上下文
# 推荐传递 app 实例,这样异步函数可以自行管理上下文的生命周期
send_security_email_async(app.app_context(), msg)
return "Test email sent asynchronously! Check your recipient's inbox."
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)关键调整:
尽管基于 threading 的异步装饰器简单有效,但在生产环境中仍需考虑以下几点:
以上就是Flask-Security-Too 异步邮件发送的重构与通用解决方案的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号