js333 > 计算机互联网 > 邮件发送已经说烂了的功能,Core中MailKit无法使用

原标题:邮件发送已经说烂了的功能,Core中MailKit无法使用

浏览次数:191 时间:2019-11-07

金沙js333娱乐场,MailKit帮助类,MailKit

public class EmailHelp
    {
        /// <summary>
        /// Smtp服务器地址
        /// </summary>
        private static readonly string SmtpServer = ConfigurationManager.AppSettings["SmtpServer"];

        /// <summary>
        /// Pop服务器地址
        /// </summary>
        private static readonly string PopServer = ConfigurationManager.AppSettings["PopServer"];

        /// <summary>
        /// Imap服务器地址
        /// </summary>
        private static readonly string ImapServer = ConfigurationManager.AppSettings["ImapServer"];

        /// <summary>
        /// SMTP端口
        /// </summary>
        private static readonly int SmtpPort = int.Parse(ConfigurationManager.AppSettings["SmtpPort"]);

        /// <summary>
        /// POP端口
        /// </summary>
        private static readonly int PopPort = int.Parse(ConfigurationManager.AppSettings["PopPort"]);

        /// <summary>
        /// IMAP端口
        /// </summary>
        private static readonly int ImapPort = int.Parse(ConfigurationManager.AppSettings["ImapPort"]);

        /// <summary>
        /// 邮件发送
        /// </summary>
        /// <param name="mailFromAccount">发送邮箱账号</param>
        /// <param name="mailPassword">发送邮箱密码</param>
        /// <param name="message">邮件</param>
        public static void SendEmali(string mailFromAccount, string mailPassword, MimeMessage message)
        {
            using (var client = new MailKit.Net.Smtp.SmtpClient())
            {
                client.Connect(SmtpServer, SmtpPort, false);

                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove("XOAUTH2");

                // Note: only needed if the SMTP server requires authentication
                client.Authenticate(mailFromAccount, mailPassword);
                client.Send(message);
                client.Disconnect(true);
            }
        }

        /// <summary>
        /// 创建文本消息
        /// </summary>
        /// <param name="fromAddress">发件地址</param>
        /// <param name="toAddressList">收件地址</param>
        /// <param name="title">标题</param>
        /// <param name="content">内容</param>
        /// <param name="IsPostFiles">是否将POST上传文件加为附件</param>
        /// <returns></returns>
        public static MimeMessage CreateTextMessage(MailboxAddress fromAddress, IList<MailboxAddress> toAddressList
            , string title, string content, bool IsPostFiles = false)
        {
            var message = new MimeMessage();
            message.From.Add(fromAddress);
            message.To.AddRange(toAddressList);
            message.Subject = title; //设置消息的主题

            var html = new TextPart("html")
            {
                Text = content,
            };
            var alternative = new Multipart("alternative");
            alternative.Add(html);

            var multipart = new Multipart("mixed");
            multipart.Add(alternative);
            if (IsPostFiles)
            {
                IList<MimePart> multiPartList = GetMimePartList();
                foreach (var item in multiPartList)
                {
                    multipart.Add(item);
                }
            }

            message.Body = multipart;
            return message;
        }

        /// <summary>
        /// 收邮件
        /// </summary>
        /// <param name="mailFromAccount">发送邮箱账号</param>
        /// <param name="mailPassword">发送邮箱密码</param>
        /// <param name="searchQuery">查询条件</param>
        /// <param name="folderName">文件夹名称</param>
        /// <returns></returns>
        public static IList<IMessageSummary> ReceiveEmail(string mailFromAccount, string mailPassword, string folderName, SearchQuery searchQuery = null)
        {
            //打开收件箱
            var folder = OpenFolder(mailFromAccount, mailPassword, folderName);

            //IList<OrderBy> orderByList = new List<OrderBy> { OrderBy.Date };
            //查询所有的邮件
            var uidss = folder.Search(searchQuery);

            IList<IMessageSummary> msgList = new List<IMessageSummary>();
            if (uidss.Count > 0)//判断是否查询到邮件
            {
                //获取邮件头
                msgList = folder.Fetch(uidss, MessageSummaryItems.UniqueId | MessageSummaryItems.Full);
            }

            folder.Close();
            return msgList;
        }


        /// <summary>
        /// 根据唯一号查询信件
        /// </summary>
        /// <param name="mailFromAccount">邮箱账号</param>
        /// <param name="mailPassword">邮箱密码</param>
        /// <param name="id">唯一号</param>
        /// <param name="folderName">文件夹名称</param>
        /// <returns></returns>
        public static MimeMessage GetEmailByUniqueId(string mailFromAccount, string mailPassword, uint id, string folderName)
        {
            //打开收件箱
            var folder = OpenFolder(mailFromAccount, mailPassword, folderName);
            UniqueId emailUniqueId = new UniqueId(id);
            MimeMessage message = folder.GetMessage(emailUniqueId);
            /*将邮件设为已读*/
            MessageFlags flags = MessageFlags.Seen;
            folder.SetFlags(emailUniqueId, flags, true);
            return message;
        }

        /// <summary>
        /// 读取上传的文件
        /// </summary>
        /// <returns></returns>
        public static IList<MimePart> GetMimePartList()
        {
            IList<MimePart> mimePartList = new List<MimePart>();
            var current = HttpContext.Current;
            if (current != null)
            {
                HttpRequest request = current.Request;
                HttpFileCollection files = request.Files;
                int filesCount = files.Count;
                for (int i = 0; i < filesCount; i++)
                {
                    HttpPostedFile item = files[i];
                    MimePart attachment = new MimePart(item.ContentType)
                    {
                        ContentObject = new ContentObject(item.InputStream, ContentEncoding.Default),
                        ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
                        ContentTransferEncoding = ContentEncoding.Base64,
                        FileName = item.FileName
                    };

                    mimePartList.Add(attachment);
                }
            }
            return mimePartList;
        }


        /// <summary>
        /// 打开邮箱文件夹
        /// </summary>
        /// <param name="mailFromAccount">邮箱账号</param>
        /// <param name="mailPassword">邮箱密码</param>
        /// <param name="folderName">文件夹名称(INBOX:收件箱名称)</param>
        /// <returns></returns>
        public static IMailFolder OpenFolder(string mailFromAccount, string mailPassword, string folderName)
        {
            ImapClient client = new ImapClient();
            client.Connect(ImapServer, ImapPort);
            client.Authenticate(mailFromAccount, mailPassword);
            //获取所有文件夹
            //List<IMailFolder> mailFolderList = client.GetFolders(client.PersonalNamespaces[0]).ToList();

            var folder = client.GetFolder(folderName);

            //打开文件夹并设置为读的方式
            folder.Open(MailKit.FolderAccess.ReadWrite);
            return folder;
        }

        /// <summary>
        /// 下载邮件附件
        /// </summary>
        /// <param name="mimePart"></param>
        public static void DownFile(MimePart mimePart)
        {
            HttpContext context = HttpContext.Current;

            // 设置编码和附件格式
            context.Response.ContentType = mimePart.ContentType.ToString();
            //context.Response.ContentEncoding = Encoding.UTF8;

            context.Response.Charset = "";
            string fileName = HttpUtility.UrlEncode(mimePart.FileName, Encoding.UTF8);
            context.Response.AppendHeader("Content-Disposition",
                "attachment;filename=" + fileName);
            using (MemoryStream ms = new MemoryStream())
            {
                mimePart.ContentObject.DecodeTo(ms);
                ms.Flush();
                ms.Position = 0;
                context.Response.BinaryWrite(ms.GetBuffer());

                context.Response.End();
            }
        }
    }

 

public class EmailHelp { /// summary /// Smtp服务器地址 /// /summary private static readonly string SmtpServer = ConfigurationManager.AppSettings[ "...

在博问中(.net core怎么实现邮件发送)知道了MailKit无法使用阿里云邮件推送服务发送邮件的问题,自已实测也遇到同样的问题,而用自己搭建的邮件服务器没这个问题。

文档目录

       我相信每一个系统,都会有邮件发送的场景,而且这种使用场景有很多,比如登入一个系统,忘记密码,那就要通过忘记密码功能,检查你注册邮件,然后发一封重设密码的邮件给你(当然现在可能通过手机验证码处理的方案多)。

于是,向阿里云提交了工单。。。在提供了TCP抓包数据后,阿里云技术专员发现了问题所在:在认证通过后,MailKit发送了EHLO命令,然后才发送MAIL FROM命令,服务器在收到EHLO时会重置客户端的认证状态,所以后续的命令过来时,服务器认为客户端没有认证,于是报错“503 Bad sequence of commands”。

 

      到今天为止我一直以为,.NET发送邮件组件有很多,我之前了解的有.Net 自带的System.Net.Mail,JMail,MimeKit&MailKit。

知道了问题的原因后,首先想到的解决方法是不让MailKit在发送MAIL FROM命令之前发送EHLO命令。

  • 简介
  • IEmailSender
    • ISmtpEmailSender
    • NullEmailSender
  • 配置
  • 集成 MailKit
    • 安装
    • 集成
    • 用法
    • 定制

今天我用度娘搜索了一下“发送邮件 组件”,搜索结果大概汇总一下,.Net 自带的,CDO 组件,JMail 组件 就这三个。没有搜索到 MimeKit&MailKit,

于是在github上签出MailKit的源代码,在 MailKitNetSmtpSmtpClient.cs 中找到了下面的代码:

 

但是单独搜“MailKit”能够搜索到,而且是大牛 张善友 写的,那我暂时先说一下 MimeKit&MailKit吧。

if (host != "smtp.strato.de" && host != "smtp.sina.com")
    Ehlo (cancellationToken);

简介

友情推荐一下 张善友  

并且发现了对应这个问题的issue:EHLO after AUTH causes send failure with 5.7.0 even though authentication succeeded #162

发送邮件是一个很常用的任务,几乎每个应用都需要。Abp提供了一个基本的框架,用于简单地发送邮件并为它分离出了邮件服务的配置。

MimeKit&MailKit 外国开源邮件发送组件,其实是由两个组件组成

原来这是已知问题,MailKit暂时采用了临时解决方法,于是我们也依葫芦画瓢,加上了阿里云邮件推送服务的SMTP服务器:

 

MimeKit 负责处理寄件者,消息,附件 等等内容相关的。

if (host != "smtp.strato.de" && host != "smtp.sina.com"
    && !host.Equals("smtp.dm.aliyun.com", StringComparison.OrdinalIgnoreCase))
    Ehlo (cancellationToken);

IEmailSender

MimeKit 官网:

接着通过以下命令自己编译出MailKit.dll(在 MailKitbinDebugnetstandard1.3 文件夹中)

它是一个你无需了解它的细节就能简单地用来发送邮件的服务,用法如下所示:

MimeKit GitHub 

git submodule update --init --recursive

cd submodulesMimeKitMimeKit
dotnet restore

cd ......MailKit
dotnet restore
dotnet build
public class TaskManager : IDomainService
{
    private readonly IEmailSender _emailSender;

    public TaskManager(IEmailSender emailSender)
    {
        _emailSender = emailSender;
    }

    public void Assign(Task task, Person person)
    {
        //Assign task to the person
        task.AssignedTo = person;

        //Send a notification email
        _emailSender.Send(
            to: person.EmailAddress,
            subject: "You have a new task!",
            body: $"A new task is assigned for you: <b>{task.Title}</b>",
            isBodyHtml: true
        );
    }
}

开发指南:

接着用这个 MailKit.dll 替换 .nugetpackagesMailKit1.4.0libnetstandard1.3MailKit.dll ,然后运行邮件发送程序,问题解决。

我们简单地注入IEmailSender,并使用Send方法,该方法有几个重载版本,其中也有能接受MailMessage对象的重载(.net core里不能用,因为.net core不包含SmtpClient和MailMessage)。

MailKit 负责发送相关的。

[更新1]

 

MailKit GitHub:

MailKit 的作者已经修复了这个问题,详见 Added another broken SMTP server to the list

ISmtpEmailSender

贴几段代码让大家感受一下

[更新2]

同样也有一个ISmtpEmailSender,它扩展了IEmailSender,添加了BuildClient方法,用来创建一个SmtpClient,然后可以直接使用SmtpClient(.net core里不能用,因为.net core不包含SmtpClient和MailMessage)。大部分情况下使用ISmtpEmailSender就已足够。

MimeKit 部分

MailKit 1.4.1 已修复这个问题。

 

var message = new MimeMessage ();
message.From.Add (new MailboxAddress ("Joey", "joey@friends.com"));
message.To.Add (new MailboxAddress ("Alice", "alice@wonderland.com"));
message.Subject = "How you doin?";

message.Body = new TextPart ("plain") {
    Text = @"Hey Alice,

What are you up to this weekend? Monica is throwing one of her parties on
Saturday and I was hoping you could make it.

Will you be my +1?

-- Joey
"
};

var message = new MimeMessage ();
message.From.Add (new MailboxAddress ("Joey", "joey@friends.com"));
message.To.Add (new MailboxAddress ("Alice", "alice@wonderland.com"));
message.Subject = "How you doin?";

// create our message text, just like before (except don't set it as the message.Body)
var body = new TextPart ("plain") {
    Text = @"Hey Alice,

What are you up to this weekend? Monica is throwing one of her parties on
Saturday and I was hoping you could make it.

Will you be my +1?

-- Joey
"
};

// create an image attachment for the file located at path
var attachment = new MimePart ("image", "gif") {
    ContentObject = new ContentObject (File.OpenRead (path), ContentEncoding.Default),
    ContentDisposition = new ContentDisposition (ContentDisposition.Attachment),
    ContentTransferEncoding = ContentEncoding.Base64,
    FileName = Path.GetFileName (path)
};

// now create the multipart/mixed container to hold the message text and the
// image attachment
var multipart = new Multipart ("mixed");
multipart.Add (body);
multipart.Add (attachment);

// now set the multipart/mixed as the message body
message.Body = multipart;

   var message = new MimeMessage ();
            message.From.Add (new MailboxAddress ("Joey", "joey@friends.com"));
            message.To.Add (new MailboxAddress ("Alice", "alice@wonderland.com"));
            message.Subject = "How you doin?";

            var builder = new BodyBuilder ();

            // Set the plain-text version of the message text
            builder.TextBody = @"Hey Alice,

What are you up to this weekend? Monica is throwing one of her parties on
Saturday and I was hoping you could make it.

Will you be my +1?

-- Joey
";

            // In order to reference sexy-pose.jpg from the html text, we'll need to add it
            // to builder.LinkedResources and then use its Content-Id value in the img src.
            var image = builder.LinkedResources.Add (@"C:UsersJoeyDocumentsSelfiessexy-pose.jpg");
            image.ContentId = MimeUtils.GenerateMessageId ();

            // Set the html version of the message text
            builder.HtmlBody = string.Format (@"<p>Hey Alice,<br>
<p>What are you up to this weekend? Monica is throwing one of her parties on
Saturday and I was hoping you could make it.<br>
<p>Will you be my +1?<br>
<p>-- Joey<br>
<center><img src=""cid:{0}""></center>", image.ContentId);

            // We may also want to attach a calendar event for Monica's party...
            builder.Attachments.Add (@"C:UsersJoeyDocumentsparty.ics");

            // Now we just need to set the message body and we're done
            message.Body = builder.ToMessageBody ();

NullEmailSender

怎么样?是不是感觉老外写得东西,比较好看,易懂啊。

NullEmailSender是IEmailSender 的Null对象设计模式的实现,可用在单元测试和属性依赖注入中。

MailKit 部分

 

using System;

using MailKit.Net.Smtp;
using MailKit;
using MimeKit;

namespace TestClient {
    class Program
    {
        public static void Main (string[] args)
        {
            var message = new MimeMessage ();
            message.From.Add (new MailboxAddress ("Joey Tribbiani", "joey@friends.com"));
            message.To.Add (new MailboxAddress ("Mrs. Chanandler Bong", "chandler@friends.com"));
            message.Subject = "How you doin'?";

            message.Body = new TextPart ("plain") {
                Text = @"Hey Chandler,

I just wanted to let you know that Monica and I were going to go play some paintball, you in?

-- Joey"
            };

            using (var client = new SmtpClient ()) {
                client.Connect ("smtp.friends.com", 587, false);

                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove ("XOAUTH2");

                // Note: only needed if the SMTP server requires authentication
                client.Authenticate ("joey", "password");

                client.Send (message);
                client.Disconnect (true);
            }
        }
    }
}

配置

这个是不是感觉跟.Net 发送邮件部分差不多啊!

邮件发送使用了设置管理系统来读取邮件发送的配置,所有设置的名称都以常量的形式定义在Abp.Net.Mail.EmailSettingNames类里。下列是它的值和描述:

下面是我封装的.Net 发送邮件部分代码,做个对比

  • Abp.Net.Mail.DefaultFromAddress:默认的邮件发送者的地址(如上面的示例)。
  • Abp.Net.Mail.DefaultFromDisplayName:默认的邮件发送者显示的名称(如上面的示例)。
  • Abp.Net.Mail.Smtp.Host: SMTP服务器的IP或域名(默认为127.0.0.1)。
  • Abp.Net.Mail.Smtp.Port: SMTP服务器的端口(默认为25)。
  • Abp.Net.Mail.Smtp.UserName: 当SMTP服务器要求认证时,需要提供的用户名。
  • Abp.Net.Mail.Smtp.Password: 当SMTP服务器要求认证时,需要提供的密码。
  • Abp.Net.Mail.Smtp.Domain: 当SMTP服务器要求认证时,需要提供的域名。
  • Abp.Net.Mail.Smtp.EnableSsl: 表明一个SMTP服务器是(true)否(false)需要使用SSL连接(默认为false)。
  • Abp.Net.Mail.Smtp.UseDefaultCredentials: 为True时使用默认的凭证代替提供的用户和密码(默认为true)。
    public class Mail
    {

        private static SystemMailHostConfig _config;
        public static Mail SystemMail
        {
            get { return new Mail(); }
        }

        public Mail()
        {
            _config = new SystemMailHostConfig
            {
                FromName = MailConfig.SystemMailFromName,
                From = MailConfig.SystemMailFrom,
                MailHostConfig = new MailHostConfig
                {
                    HostName = MailConfig.SmtpHostName,
                    Account = MailConfig.MailAccount,
                    Password = MailConfig.MailPassword
                }
            };
        }

        public Mail(SystemMailHostConfig config)
        {
            _config = config;
        }

        /// <summary>
        /// 发送邮件
        /// </summary>
        /// <param name="context"></param>
        /// <param name="message">默认为null。 System.Net.Mail.MailMessage </param>
        public void Send(MailContext context, MailMessage message = null)
        {
            //ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult;
            var sender = new SmtpClient();
            message = message ?? new MailMessage();
            if (context.Receivers.IsNotNullOrEmpty())
            {
                message.To.AddRange(context.Receivers.ToArray());
            }
            if (context.Ccs.IsNotNullOrEmpty())
            {
                message.CC.AddRange(context.Ccs.ToArray());
            }
            if (context.Bccs.IsNotNullOrEmpty())
            {
                message.Bcc.AddRange(context.Bccs.ToArray());
            }
            message.Subject = context.Subject;
            message.Body = context.Body;

            if (context.Attachments.IsNotNullOrEmpty())
            {
                message.Attachments.AddRange(context.Attachments.ToArray());
            }
            message.IsBodyHtml = true;
            try
            {
                message.From = new MailAddress(_config.From, _config.FromName);
                sender.Port = 2620;
                sender.Host = _config.MailHostConfig.HostName;
                sender.UseDefaultCredentials = false;
                sender.Credentials = new NetworkCredential(_config.MailHostConfig.Account, _config.MailHostConfig.Password);
                sender.DeliveryMethod = SmtpDeliveryMethod.Network;
                sender.EnableSsl = true;
                sender.Send(message);
            }
            catch
            {
                _config = new SystemMailHostConfig
                {
                    FromName = MailConfig.StandbySystemMailFromName,
                    From = MailConfig.StandbySystemMailFrom,
                    MailHostConfig = new MailHostConfig
                    {
                        HostName = MailConfig.StandbySmtpHostName,
                        Account = MailConfig.StandbyMailAccount,
                        Password = MailConfig.StandbyMailPassword
                    }
                };
                message.From = new MailAddress(_config.From, _config.FromName);
                sender.Port = 587;
                sender.Host = _config.MailHostConfig.HostName;               
                sender.UseDefaultCredentials = false;
                sender.Credentials = new NetworkCredential(_config.MailHostConfig.Account, _config.MailHostConfig.Password);
                sender.DeliveryMethod = SmtpDeliveryMethod.Network;
                sender.EnableSsl = true;
                sender.Send(message);
            }
        }
    }

 

MimeKit 和 MailKit 支持最新的国际化的电子邮件标准,是.NET 中为一个支持完整支持这些标准电子邮件库,且它还支持.NET/Mono的所有平台,包括移动电话、平板等,希望大家在架构多一种方案,多一种选择。

集成 MailKit

 

由于.net core不支持标准的System.Net.Mail.SmtpClient,所以我们需要第三方供应商来发送邮件,幸运地是,MailKit 是默认的Smtpclient的一个很好的代替,而且微软也建议使用它。

Abp.MailKit 包优雅地集成到了Abp的邮件发送系统里,所以,你仍可像前面的方式通过MailKit使用IEmailSender。

 

安装

首先,安装Abp.MailKit 包到你的项目:

Install-Package Abp.MailKit

 

集成

添加AbpMailKitModule 依赖到你的模块:

[DependsOn(typeof(AbpMailKitModule))]
public class MyProjectModule : AbpModule
{
    //...
}

 

用法

你可以像前面描述的那样使用IEmailSender,因为Abp.MailKit包为它注册了MailKit的实现。也使用上面定义的配置。

 

定制

在创建MailKit的SmtpClient时,你可能会有额外的配置或自己的定制,此时,你可以用你自己的实现替换IMailKitSmtpBuilder 接口的注册,不过可以通过继承DefaultMailKitSmtpBuilder 更简单。比如,你想为所有的SSL连接提供一个凭证,这种情况下,你可以重写ConfigureClient方法,如下所示:

public class MyMailKitSmtpBuilder : DefaultMailKitSmtpBuilder
{
    public MyMailKitSmtpBuilder(ISmtpEmailSenderConfiguration smtpEmailSenderConfiguration) 
        : base(smtpEmailSenderConfiguration)
    {
    }

    protected override void ConfigureClient(SmtpClient client)
    {
        client.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;

        base.ConfigureClient(client);
    }
}

然后在你的模块的PreInitialize方法中用你上面的实现替换IMailKitStmpBuilder接口的实现:

[DependsOn(typeof(AbpMailKitModule))]
public class MyProjectModule : AbpModule
{
    public override void PreInitialize()
    {
        Configuration.ReplaceService<IMailKitSmtpBuilder, MyMailKitSmtpBuilder>();
    }

    //...
}

(记得添加"using Abp.Configuration.Startup;"声明,因为ReplaceService 的扩展方法定义在这个命名空间里)。

本文由js333发布于计算机互联网,转载请注明出处:邮件发送已经说烂了的功能,Core中MailKit无法使用

关键词:

上一篇:没有了

下一篇:没有了