使用Django发送邮件
来源:互联网 发布:网络不谢是什么意思 编辑:程序博客网 时间:2024/05/18 03:18
引言
在日常开发中,通过程序来实现对邮箱的操作是一个很常见的应用场景。比如:
- 通过邮件来确认用户注册
- 后台管理人员针对用户的反馈发送邮件
- 通过邮箱来重置用户密码
那么,笔者简单介绍一下如何使用Django实现邮件的发送。
PS:笔者在这里推荐一篇掘金的文章是关于邮箱的登录注册的设计细节,其中一些细节考虑的挺好的,在设计的时候可以用来参考。
传送门:干嘛又要邮箱登录啊?—现代登录系统的结构设计
准备
电子邮件系统
在一个因特网电子邮件系统中,通常有三个组成部分:
- 用户代理(User Agent)
- 比如一些第三方客户端,如foxmail,以及常用的浏览器
- 邮件服务器(Mail Server)
- 比如QQ邮箱的邮箱服务器,域名为stmp.qq.com
- 简单邮件传输协议(SMTP)
- 在邮件服务器中互相通讯所采取的协议
那么,这三个组成部分是如何运行的呢?
假设用户A通过用户代理(QQ邮箱)向用户B(163邮箱)发出了一封电子邮件,这个过程可以描述为:
- 用户A的用户代理通过SMTP协议登陆QQ邮箱的邮件服务器,并将要发送的内容放在了QQ邮箱的邮件服务器的属于用户A的邮箱中。
- QQ邮箱的邮件服务器通过SMTP协议向163邮箱的邮件服务器发送这封电子邮件。
- 163邮箱的邮件服务器在收到电子邮件之后根据邮件的头部得知接收者是用户B,将邮件放到了邮件服务器中属于用户B的邮箱中。
- 用户B在用户代理中登录了163的邮件服务器,并通过POP3或者IMAP等“拉”协议拉取邮件服务器中自己的邮件到用户代理中查看。(若是浏览器,可以通过HTTP协议向邮件服务器传送邮件或者拉取邮件)
流程图如下:
在这里有一个小小的问题,为什么不是用户A的用户代理直接向用户B的用户代理直接发送邮件,而要通过邮件服务器进行发送呢?
邮件服务器在邮件的传送与拉取中有两个重要作用:
- 尽可能的保证在任何时刻维持开机状态,接受来自其他邮件服务器的邮件传输。
- 当邮件发送失败后,继续尝试向对方邮件服务器发送,若邮件发送失败,通知用户代理。
SMTP协议与Python标准库:smtplib
SMTP协议出现的时期比HTTP协议还要早,因此,也有部分设计的不够完善的地方。比如SMTP协议要求传输的邮件内容以及邮件头部都需要使用ASCII编码,导致其他无法用ASCII编码的字符(如汉字,或二进制文件)必须通过base64等方法将字节转换为ascii码才可以传输,有兴趣的同学可以去了解SMTP的扩展协议MIME,MIME也同样广泛的使用在HTTP协议中。
对于传送一般文本文件与二进制文件有什么区别,可以去参考这个知乎问题:关于smtp协议和mime,困惑好久的问题:为什么一定要传送可见的字符?
在Python中,smtplib标准库主要负责邮件的发送,而邮件内容和头部的封装和解释主要通过email标准库负责。
简单了解一下stmplib中两个用于发送邮件的api:
- sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])
- send_message(msg, from_addr, to_addrs, mail_options=[], rcpt_options=[])
这两个函数的区别是在sendmail函数中的msg参数是一个string,这意味着在发送的时候必须是符合SMTP协议的完整报文字符串(比如如果为邮件头部,则使用\r\n作为一行的结束,邮件头部和邮件内容的区分即一个空行,即以\r\n\r\n作为区分)。
而对于send_message函数,msg参数传入的是email标准库中的Message对象,Message对象提供了对邮件头部和内容的api,而无需开发者担心SMTP协议报文的具体格式。(可以调用Message.as_string()查看输出的SMTP报文格式)
这里还有一点稍微需要注意的是,当设置了这两个函数的from_addr和to_addrs参数之后,在msg参数中还需要再设置一遍,因为当查看邮件的时候,邮件显示的发送方和接收方其实是由邮件头部来决定的,即在msg参数中。
下面笔者给出一个使用smtplib登录QQ邮箱并发送邮件的例子:
# SSL port for QQ emailsmtp_ssl_port = 465# QQ email server domainsmtp_server = "smtp.qq.com"smtp_from_user = "from_user@qq.com"# not your QQ passwordsmtp_password = "password"smtp_to_user = 'to_user@qq.com'smtp_message_string = """From: %s\r\nSubject: Hello\r\n\r\nHello, %s\r\n. \r\n""" % (smtp_from_user, smtp_to_user)import smtplib# construct a connection to QQ email serverserver = smtplib.SMTP_SSL(smtp_server, smtp_ssl_port)# login with your QQ account and passwordserver.login(smtp_from_user, smtp_password)# message is a stringserver.sendmail(smtp_from_user, smtp_to_user, smtp_message)# message is a objectfrom email.message import Messagemes = Message()mes.add_header('From', smtp_from_user)mes.add_header('Subject', "Hello")mes.set_payload("Hello, %s" % smtp_to_user)server.send_message(mes, smtp_from_user, smtp_to_user)server.quit()
对于QQ邮箱的登录还有一个值得注意的地方,你需要在QQ邮箱的用户设置中开启SMTP服务,然后获取一个授权码,通过授权码来登录SMTP服务器,不要使用QQ密码来登录服务器。
如果需要在邮件中传送其他类型的文件,则需要使用MIME类型,在email标准库中也提供了封装的子类。
Python官方参考文档:
19.1. email — An email and MIME handling package
21.17. smtplib — SMTP protocol client
风险:邮件头部注入
在前面笔者已经提过SMTP协议中使用\r\n作为换行分隔符,因此,如果攻击者在表单上输入邮件地址时加上换行符就能起到执行其他命令的效果,所以,在检查提交表单的相关邮件头部的时候需要禁止换行符的输入。
关于Django应对邮件头部的做法在Django book中也有提及:Django book 第二十章:安全
Django邮件服务组成
Django的邮件服务由三个部分组成:
- send_mail以及send_mass_mail两个简易的函数接口
- EmailMessage对象负责对邮件的头部和内容构造
- Email backends负责发送邮件
Django send_mail()
Django的发送邮件邮件服务是基于smtplib封装的,并提供了两个简单的api接口:
- send_mail()
- send_mass_mail()
这两个函数的区别主要是在接收方看到的接收列表是否会显示其余接收者,以及前者每发送一次邮件就需要建立一次连接,后者发送所有的邮件都只需建立一次连接。
然而查看这两个函数的源码,可以看到如下注释:
"""Note: The API for this method is frozen. New code wanting to extend the functionality should use the EmailMessage class directly."""
Django官方更为推荐的是使用EmailMessage对象:
Note
This is a design feature. send_mail() and related functions were originally the only interface Django provided. However, the list of parameters they accepted was slowly growing over time. It made sense to move to a more object-oriented design for email messages and retain the original functions only for backwards compatibility.
Django EmailMessage
Django的EmailMessage对象提供了更为强大的服务,其中包括了cc以及bcc的相关邮件服务(不知道什么是cc和bcc?这里有知乎传送门:电子邮件的抄送和密送的有什么作用?)。
Django email backends
Django提供了四个backend,分别用于各种场景,如开发,实际生产:
- SMTP backend
- Console backend
- File backend
- In-memory backend
- Dummy backend
在这里,值得注意的是,如果是在django test的环境下测试邮件的发送是不会真正的把邮件发送出去的,因为在test模式下使用的并非是SMTP backend,但是会返回正常的邮箱发送成功的标志。
参考文档:
Django Sending email
Django testing email
- 使用Django发送邮件
- Django中使用多线程发送邮件
- 使用 django channels 作为邮件发送队列
- django使用QQ企业邮箱发送邮件
- python 使用Django 的 邮件模块 发送邮件
- Django发送邮件
- Django发送html邮件
- django 中发送邮件
- django 发送邮件
- django发送邮件
- Django 发送邮件
- Django 发送邮件配置
- django邮件发送
- django发送邮件
- Django 应用 -- 发送邮件
- Django中发送邮件send_mail
- Django的邮件发送功能
- Django 表单及邮件发送
- Python删除列表中重复元素
- app版本更新
- Python 核心编程第七章练习题
- IDL中一些容易忘的东西,随用随记
- std::function 使用
- 使用Django发送邮件
- iOSImagesExtractor(获取App上的所有图片)
- 【自用】关于surfaceview调用宿主Activity中的方法
- 洛谷1313 计算系数
- 关于动态生成的标签无法绑定事件
- Android 5.1-s5p6818平台总结-编译时候出现“intermediates/src/R.stamp] ”的问题
- 谈谈8583报文的使用及测试
- 如何转载CSDN文章
- 数据库服务器的性能调优-续