@wddpct
2017-04-03T13:23:37.000000Z
字数 8377
阅读 2421
最近pm临时提出了多种邮件验证操作的需求,因为一时间也没有找到好的邮件收发组件,也抱着研究ABP的心态,就花了几小时时间探究了一下ABP中关于Email的处理和操作。其实邮件操作大多大同小异,这次只是希望介绍一下ABP中实现功能的代码结构而已,以下是具体过程
演示的ABP代码版本为0.9.0.0,不过后面版本对于这部分的修改较少,所以完全不影响之后版本的移植使用
ABP的Mail操作放在了Abp.Net.Mail和Abp.Net.Mail.Smtp中,第一步先让我们直接看看这个文件夹下类及接口的代码图(未经允许不可使用)
根据代码图可以发现ABP对于Mail处理主要由三部分组成
至于Smtp开头的文件,则是以Smtp形式进行邮件发送的一种实现文件而已,后文也将直接使用该种方式进行处理
在具体的实现上,我发现ABP本身的Mail相关类已经十分完整,只是在邮件参数的配置上需要采取自定义的实现,所以我直接抽取了ABP的源码来进行演示
AppSettingNames中定义相关的唯一字符串,大家可以认为是Key,而AppSettingProvider中则是将Key对应的邮件参数赋值,供之后的Configuration读取
邮件功能推荐放在Core模块中,完成相关的provider后在CoreModule加入
Configuration.Settings.Providers.Add<AppSettingProvider>();即可生效
public static class AppSettings{/// <summary>/// SMTP related email settings./// </summary>public static class Smtp{/// <summary>/// Abp.Net.Mail.DefaultFromAddress/// </summary>public const string DefaultAddress = "Trucking.Net.Mail.DefaultFromAddress";/// <summary>/// Abp.Net.Mail.DefaultFromDisplayName/// </summary>public const string DefaultDisplayName = "Trucking.Net.Mail.DefaultFromDisplayName";/// <summary>/// Abp.Net.Mail.Smtp.Host/// </summary>public const string Host = "Trucking.Net.Mail.Smtp.Host";/// <summary>/// Abp.Net.Mail.Smtp.Port/// </summary>public const string Port = "Trucking.Net.Mail.Smtp.Port";/// <summary>/// Abp.Net.Mail.Smtp.UserName/// </summary>public const string UserName = "Trucking.Net.Mail.Smtp.UserName";/// <summary>/// Abp.Net.Mail.Smtp.Password/// </summary>public const string Password = "Trucking.Net.Mail.Smtp.Password";/// <summary>/// Abp.Net.Mail.Smtp.Domain/// </summary>public const string Domain = "Trucking.Net.Mail.Smtp.Domain";/// <summary>/// Abp.Net.Mail.Smtp.EnableSsl/// </summary>public const string EnableSsl = "Trucking.Net.Mail.Smtp.EnableSsl";/// <summary>/// Abp.Net.Mail.Smtp.UseDefaultCredentials/// </summary>public const string UseDefaultCredentials = "Trucking.Net.Mail.Smtp.UseDefaultCredentials";}}public class AppSettingProvider : SettingProvider{public override IEnumerable<SettingDefinition> GetSettingDefinitions(SettingDefinitionProviderContext context){return new[]{new SettingDefinition(AppSettings.Smtp.Host, "smtp.gmail.com", L("SmtpHost"),scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(AppSettings.Smtp.Port, "587", L("SmtpPort"),scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(AppSettings.Smtp.UserName, "myemail@gmail.com", L("Username"),scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(AppSettings.Smtp.Password, "mypassword", L("Password"),scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(AppSettings.Smtp.Domain, "", L("DomainName"),scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(AppSettings.Smtp.EnableSsl, "true", L("UseSSL"),scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(AppSettings.Smtp.UseDefaultCredentials, "false", L("UseDefaultCredentials"),scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(AppSettings.Smtp.DefaultAddress, "myemail@gmail.com",L("DefaultEmailAddress"), scopes: SettingScopes.Application | SettingScopes.Tenant),new SettingDefinition(AppSettings.Smtp.DefaultDisplayName, "CompanyName",L("DefaultDisplayName"), scopes: SettingScopes.Application | SettingScopes.Tenant)};}private static LocalizableString L(string name){return new LocalizableString(name, AbpConsts.LocalizationSourceName);}}
这个类的作用如上面提到的那样,主要是读取自定义的AppSettingProvider中设置的邮件参数值
IUserEmailSenderConfiguration接口略
public class UserEmailSenderConfiguration : TruckingServiceBase, IUserEmailSenderConfiguration, ITransientDependency{/// <summary>/// Gets a setting value by checking. Throws <see cref="AbpException"/> if it's null or empty./// </summary>/// <param name="name">Name of the setting</param>/// <returns>Value of the setting</returns>protected string GetNotEmptySettingValue(string name){var value = SettingManager.GetSettingValue(name);if (value.IsNullOrEmpty()){throw new AbpException(String.Format("Setting value for '{0}' is null or empty!", name));}return value;}/// <summary>/// SMTP Host name/IP./// </summary>public string Host{get { return GetNotEmptySettingValue(AppSettings.Smtp.Host); }}/// <summary>/// SMTP Port./// </summary>public int Port{get { return SettingManager.GetSettingValue<int>(AppSettings.Smtp.Port); }}/// <summary>/// User name to login to SMTP server./// </summary>public string UserName{get { return GetNotEmptySettingValue(AppSettings.Smtp.UserName); }}/// <summary>/// Password to login to SMTP server./// </summary>public string Password{get { return GetNotEmptySettingValue(AppSettings.Smtp.Password); }}/// <summary>/// Domain name to login to SMTP server./// </summary>public string Domain{get { return SettingManager.GetSettingValue(AppSettings.Smtp.Domain); }}/// <summary>/// Is SSL enabled?/// </summary>public bool EnableSsl{get { return SettingManager.GetSettingValue<bool>(AppSettings.Smtp.EnableSsl); }}/// <summary>/// Use default credentials?/// </summary>public bool UseDefaultCredentials{get { return SettingManager.GetSettingValue<bool>(AppSettings.Smtp.UseDefaultCredentials); }}public string DefaultAddress{get { return GetNotEmptySettingValue(AppSettings.Smtp.DefaultAddress); }}public string DefaultDisplayName{get { return SettingManager.GetSettingValue(AppSettings.Smtp.DefaultDisplayName); }}}
UserSmtpEmailSender类才是真正的对Mail操作类,它通过注入IUserEmailSenderConfiguration接口,读取相关的Mail参数,如Host,UserName,Password等,然后再调用.NET的Mail发送邮件。
IUserSmtpEmailSender接口略
public class UserSmtpEmailSender : IUserSmtpEmailSender, ITransientDependency{private readonly IUserEmailSenderConfiguration _configuration;public UserSmtpEmailSender(IUserEmailSenderConfiguration configuration){_configuration = configuration;}public async Task SendAsync(string to, string subject, string body, bool isBodyHtml = true){await SendAsync(_configuration.DefaultAddress, to, subject, body, isBodyHtml);}public void Send(string to, string subject, string body, bool isBodyHtml = true){Send(_configuration.DefaultAddress, to, subject, body, isBodyHtml);}public async Task SendAsync(string from, string to, string subject, string body, bool isBodyHtml = true){await SendAsync(new MailMessage(from, to, subject, body) {IsBodyHtml = isBodyHtml});}public void Send(string from, string to, string subject, string body, bool isBodyHtml = true){Send(new MailMessage(from, to, subject, body) {IsBodyHtml = isBodyHtml});}public async Task SendAsync(MailMessage mail, bool normalize = true){if (normalize)NormalizeMail(mail);await SendEmailAsync(mail);}public void Send(MailMessage mail, bool normalize = true){if (normalize)NormalizeMail(mail);SendEmail(mail);}public SmtpClient BuildClient(){var host = _configuration.Host;var port = _configuration.Port;var smtpClient = new SmtpClient(host, port);try{if (_configuration.EnableSsl)smtpClient.EnableSsl = true;if (_configuration.UseDefaultCredentials){smtpClient.UseDefaultCredentials = true;}else{smtpClient.UseDefaultCredentials = false;var userName = _configuration.UserName;if (!userName.IsNullOrEmpty()){var password = _configuration.Password;var domain = _configuration.Domain;smtpClient.Credentials = !domain.IsNullOrEmpty()? new NetworkCredential(userName, password, domain): new NetworkCredential(userName, password);}}return smtpClient;}catch{smtpClient.Dispose();throw;}}/// <summary>/// Normalizes given email./// Fills <see cref="MailMessage.From" /> if it's not filled before./// Sets encodings to UTF8 if they are not set before./// </summary>/// <param name="mail">Mail to be normalized</param>protected virtual void NormalizeMail(MailMessage mail){if ((mail.From == null) || mail.From.Address.IsNullOrEmpty())mail.From = new MailAddress(_configuration.DefaultAddress,_configuration.DefaultDisplayName,Encoding.UTF8);if (mail.HeadersEncoding == null)mail.HeadersEncoding = Encoding.UTF8;if (mail.SubjectEncoding == null)mail.SubjectEncoding = Encoding.UTF8;if (mail.BodyEncoding == null)mail.BodyEncoding = Encoding.UTF8;}protected async Task SendEmailAsync(MailMessage mail){using (var smtpClient = BuildClient()){await smtpClient.SendMailAsync(mail);}}protected void SendEmail(MailMessage mail){using (var smtpClient = BuildClient()){smtpClient.Send(mail);}}}
之后我们只需要再调用该EmailSender的SendAsync,填入对应的参数,亲测有效。如果之后要更换邮件组件,则只需要实现对应的UserLibraryEmailSerder即可。
至此,我们便将ABP中单独的一个邮件功能抽离了出来并做了相关解释,其实只要花点功夫,自己手动剥离代码图也可以理解了。至于一个简单的邮件功能为什么在ABP中要实现得如此复杂,每个程序员有每个程序员的答案,还是继续学习吧