You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:09:25