Flask使用Flask-Mail发送HTML表单数据及报错问题排查
Flask-Mail发送HTML表单提交数据的落地实现方案
报错根因
UnicodeEncodeError:Flask-Mail默认未强制使用UTF-8编码,处理中文等非ASCII字符时会回退到ASCII编码,超出编码范围就会抛错KeyError: 'mail':0.9.1版本的Flask-Mail实例未正确绑定到当前Flask应用的扩展注册表,一般是初始化顺序错误、未传入app实例导致
完整实现步骤
1. 安装对应版本依赖
pip install flask flask-mail==0.9.1
2. 后端逻辑(main.py)
注意初始化顺序:先创建Flask实例、加载配置,再绑定Mail实例,配置中显式指定UTF-8编码即可解决两个报错。
from flask import Flask, render_template, request, flash, redirect from flask_mail import Mail, Message app = Flask(__name__) app.secret_key = "替换为自定义的随机密钥即可" # 邮件服务配置,替换为自己的邮箱服务商参数 app.config.update( MAIL_SERVER="smtp.qq.com", # 以QQ邮箱为例,其他服务商替换对应smtp地址 MAIL_PORT=465, MAIL_USE_SSL=True, MAIL_USERNAME="你的发件邮箱地址", MAIL_PASSWORD="你的邮箱授权码(不是登录密码)", MAIL_DEFAULT_SENDER="你的发件邮箱地址", # 核心配置:强制UTF-8编码,解决非ASCII字符编码报错 MAIL_CHARSET="utf-8", MAIL_ASCII_ATTACHMENTS=False ) # 核心:传入app实例完成绑定,顺序不能错,解决KeyError: 'mail' mail = Mail(app) @app.route("/contact", methods=["GET", "POST"]) def contact(): if request.method == "POST": # 读取表单字段,做基础空值校验 name = request.form.get("name", "").strip() contact_email = request.form.get("email", "").strip() content = request.form.get("message", "").strip() if not all([name, contact_email, content]): flash("请填写完整所有必填项") return redirect("/contact") # 构造邮件内容,支持直接拼接中文字段 msg = Message( subject=f"网站新留言 - 来自{name}", recipients=["你的收件邮箱地址"], # 纯文本版正文 body=f""" 收到新的用户联系留言: 姓名:{name} 联系邮箱:{contact_email} 留言内容: {content} """ ) # 可选:配置HTML格式正文,样式更美观 msg.html = f""" <div style="max-width: 600px; margin: 0 auto; padding: 20px; font-family: sans-serif;"> <h2 style="border-bottom: 1px solid #eee; padding-bottom: 10px;">新的联系表单留言</h2> <p><strong>提交人姓名:</strong>{name}</p> <p><strong>联系邮箱:</strong>{contact_email}</p> <p><strong>留言内容:</strong></p> <div style="background: #f5f7fa; padding: 15px; border-radius: 6px; white-space: pre-wrap;">{content}</div> </div> """ mail.send(msg) flash("留言提交成功,我们会尽快与你联系") return redirect("/contact") return render_template("contact.html") if __name__ == "__main__": app.run(debug=True)
3. 前端表单模板(templates/contact.html)
确保表单使用POST方法提交,字段name属性和后端读取的字段名一致即可:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>联系我们</title> <style> .form-item { margin: 15px 0; } .tip { padding: 10px; border-radius: 4px; } .success { background: #e7f6ed; color: #1a7f37; } </style> </head> <body> <h1>联系我们</h1> {% with messages = get_flashed_messages() %} {% if messages %} {% for msg in messages %} <div class="tip success">{{ msg }}</div> {% endfor %} {% endif %} {% endwith %} <form method="POST" action="/contact"> <div class="form-item"> <label>姓名:</label> <input type="text" name="name" required> </div> <div class="form-item"> <label>联系邮箱:</label> <input type="email" name="email" required> </div> <div class="form-item"> <label>留言内容:</label> <textarea name="message" rows="6" required></textarea> </div> <button type="submit">提交留言</button> </form> </body> </html>
避坑提示
- 如果使用Flask工厂模式开发,创建Mail实例后必须调用
mail.init_app(app)完成绑定,否则会触发KeyError: 'mail' - 国内邮箱服务(QQ、163等)需要在邮箱后台开启SMTP服务,使用生成的授权码作为登录密码,不能直接用账号登录密码
- 不要直接通过
request.form['字段名']读取表单值,字段缺失时会直接抛出500错误,用request.form.get()更稳妥 - 如果需要在邮件正文中展示用户提交的富文本内容,记得做XSS过滤,避免恶意脚本注入
内容的提问来源于stack exchange,提问作者StudioMan
相关产品推荐
相关产品推荐

