使用FastAPI-Mail后台任务发送用户注册验证邮件无反应且无报错问题求助
我正在开发一个FastAPI的用户注册路由,预期完成用户注册后自动向注册邮箱发送验证邮件,但目前邮件并未发送,且控制台没有任何报错信息。以下是我的相关代码,麻烦帮忙排查下问题所在:
注册路由代码
@router.post('/register') async def register_user(data: MakeUser, background_task: BackgroundTasks, request: Request, db: Session = Depends(get_db)): try: # NOTE: It is better to go with this right now in future avoid making multiple querys if user_crud.validate_email(data.email, db): return JSONResponse("Email Already Exist", status.HTTP_406_NOT_ACCEPTABLE) if user_crud.validate_username(data.username, db): return JSONResponse( "Username is taken", status_code=status.HTTP_406_NOT_ACCEPTABLE) data = user_crud.create_user(data, db) email_verification_token = create_token_for_email_verification( {"user_id": data.public_id, "email": data.email}) email_verification_url = f'{request.base_url}auth/verify/{email_verification_token}' await send_email_verification_mail( background_task, "Email Verification", data.email, {"url": email_verification_url}) return data except Exception as e: print(e) return HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Problem creating user")
邮件发送脚本
async def send_email_verification_mail(background_task: BackgroundTasks, subject: str, email_to: str, body): print(os.environ.get("MAIL_USERNAME")) message = MessageSchema( subject=subject, recipients=[email_to], body=body ) fm = FastMail(configs) print("Sendig Mail") background_task.add_task(fm.send_message, message) print("Mail Sent")
我帮你梳理几个可能的问题点,你可以逐一排查:
背景任务未执行:FastAPI的
BackgroundTasks是在请求返回给客户端之后才会运行的。如果你的应用在请求结束后就终止了(比如本地开发用uvicorn --reload时,代码热重载导致进程重启,或者测试时请求完成后直接关闭了服务),那背景任务根本没机会执行。你可以先临时去掉背景任务,直接在路由里调用await fm.send_message(message),看看邮件能不能正常发送,先排除背景任务的影响。邮件配置不完整/错误:你打印了
MAIL_USERNAME,但要确认所有邮件相关的环境变量(比如MAIL_PASSWORD、MAIL_SERVER、MAIL_PORT、MAIL_STARTTLS/MAIL_SSL)都配置正确。不同邮箱服务商的要求不一样,比如Gmail需要启用应用专用密码,QQ邮箱需要开启POP3/SMTP服务。你可以写个简单的测试脚本,直接用SMTP库验证配置是否能正常发送邮件:import os import smtplib from email.mime.text import MIMEText def test_smtp_config(): msg = MIMEText("这是一封测试邮件") msg["Subject"] = "邮件配置测试" msg["From"] = os.environ.get("MAIL_USERNAME") msg["To"] = "你的测试邮箱地址" try: with smtplib.SMTP(os.environ.get("MAIL_SERVER"), int(os.environ.get("MAIL_PORT"))) as server: if os.environ.get("MAIL_STARTTLS") == "True": server.starttls() server.login(os.environ.get("MAIL_USERNAME"), os.environ.get("MAIL_PASSWORD")) server.send_message(msg) print("测试邮件发送成功!") except Exception as e: print(f"发送失败,错误信息:{e}") test_smtp_config()MessageSchema参数错误:FastMail的
MessageSchema如果要发送HTML邮件,应该使用html参数而不是body(body一般对应纯文本内容)。你传入的body是一个字典{"url": email_verification_url},这显然不是合法的邮件内容。你需要先把这个字典渲染成HTML或纯文本模板,比如用Jinja2:# 先定义模板 from jinja2 import Template verification_template = Template("请点击链接验证邮箱:<a href='{{ url }}'>{{ url }}</a>") html_content = verification_template.render(body) message = MessageSchema( subject=subject, recipients=[email_to], html=html_content # 用html参数代替body )背景任务中的异常未被捕获:
fm.send_message如果执行出错,因为是在背景任务里运行,异常不会被路由里的try-except捕获,所以你看不到报错。可以给背景任务的任务函数加上异常处理,或者临时直接调用await fm.send_message(message),看看有没有报错信息。
内容的提问来源于stack exchange,提问作者Tazim Rahbar

