[关闭]
@wddpct 2017-04-03T21:23:37.000000Z 字数 8377 阅读 2021

1. 前言

最近pm临时提出了多种邮件验证操作的需求,因为一时间也没有找到好的邮件收发组件,也抱着研究ABP的心态,就花了几小时时间探究了一下ABP中关于Email的处理和操作。其实邮件操作大多大同小异,这次只是希望介绍一下ABP中实现功能的代码结构而已,以下是具体过程

演示的ABP代码版本为0.9.0.0,不过后面版本对于这部分的修改较少,所以完全不影响之后版本的移植使用

2. 实现过程

ABP的Mail操作放在了Abp.Net.Mail和Abp.Net.Mail.Smtp中,第一步先让我们直接看看这个文件夹下类及接口的代码图(未经允许不可使用)

1. 代码图(重)

根据代码图可以发现ABP对于Mail处理主要由三部分组成

至于Smtp开头的文件,则是以Smtp形式进行邮件发送的一种实现文件而已,后文也将直接使用该种方式进行处理

2.具体实现

在具体的实现上,我发现ABP本身的Mail相关类已经十分完整,只是在邮件参数的配置上需要采取自定义的实现,所以我直接抽取了ABP的源码来进行演示

2.1 定义AppSettingNames及AppSettingProvider

AppSettingNames中定义相关的唯一字符串,大家可以认为是Key,而AppSettingProvider中则是将Key对应的邮件参数赋值,供之后的Configuration读取

邮件功能推荐放在Core模块中,完成相关的provider后在CoreModule加入Configuration.Settings.Providers.Add<AppSettingProvider>();即可生效

  1. public static class AppSettings
  2. {
  3. /// <summary>
  4. /// SMTP related email settings.
  5. /// </summary>
  6. public static class Smtp
  7. {
  8. /// <summary>
  9. /// Abp.Net.Mail.DefaultFromAddress
  10. /// </summary>
  11. public const string DefaultAddress = "Trucking.Net.Mail.DefaultFromAddress";
  12. /// <summary>
  13. /// Abp.Net.Mail.DefaultFromDisplayName
  14. /// </summary>
  15. public const string DefaultDisplayName = "Trucking.Net.Mail.DefaultFromDisplayName";
  16. /// <summary>
  17. /// Abp.Net.Mail.Smtp.Host
  18. /// </summary>
  19. public const string Host = "Trucking.Net.Mail.Smtp.Host";
  20. /// <summary>
  21. /// Abp.Net.Mail.Smtp.Port
  22. /// </summary>
  23. public const string Port = "Trucking.Net.Mail.Smtp.Port";
  24. /// <summary>
  25. /// Abp.Net.Mail.Smtp.UserName
  26. /// </summary>
  27. public const string UserName = "Trucking.Net.Mail.Smtp.UserName";
  28. /// <summary>
  29. /// Abp.Net.Mail.Smtp.Password
  30. /// </summary>
  31. public const string Password = "Trucking.Net.Mail.Smtp.Password";
  32. /// <summary>
  33. /// Abp.Net.Mail.Smtp.Domain
  34. /// </summary>
  35. public const string Domain = "Trucking.Net.Mail.Smtp.Domain";
  36. /// <summary>
  37. /// Abp.Net.Mail.Smtp.EnableSsl
  38. /// </summary>
  39. public const string EnableSsl = "Trucking.Net.Mail.Smtp.EnableSsl";
  40. /// <summary>
  41. /// Abp.Net.Mail.Smtp.UseDefaultCredentials
  42. /// </summary>
  43. public const string UseDefaultCredentials = "Trucking.Net.Mail.Smtp.UseDefaultCredentials";
  44. }
  45. }
  46. public class AppSettingProvider : SettingProvider
  47. {
  48. public override IEnumerable<SettingDefinition> GetSettingDefinitions(SettingDefinitionProviderContext context)
  49. {
  50. return new[]
  51. {
  52. new SettingDefinition(AppSettings.Smtp.Host, "smtp.gmail.com", L("SmtpHost"),
  53. scopes: SettingScopes.Application | SettingScopes.Tenant),
  54. new SettingDefinition(AppSettings.Smtp.Port, "587", L("SmtpPort"),
  55. scopes: SettingScopes.Application | SettingScopes.Tenant),
  56. new SettingDefinition(AppSettings.Smtp.UserName, "myemail@gmail.com", L("Username"),
  57. scopes: SettingScopes.Application | SettingScopes.Tenant),
  58. new SettingDefinition(AppSettings.Smtp.Password, "mypassword", L("Password"),
  59. scopes: SettingScopes.Application | SettingScopes.Tenant),
  60. new SettingDefinition(AppSettings.Smtp.Domain, "", L("DomainName"),
  61. scopes: SettingScopes.Application | SettingScopes.Tenant),
  62. new SettingDefinition(AppSettings.Smtp.EnableSsl, "true", L("UseSSL"),
  63. scopes: SettingScopes.Application | SettingScopes.Tenant),
  64. new SettingDefinition(AppSettings.Smtp.UseDefaultCredentials, "false", L("UseDefaultCredentials"),
  65. scopes: SettingScopes.Application | SettingScopes.Tenant),
  66. new SettingDefinition(AppSettings.Smtp.DefaultAddress, "myemail@gmail.com",
  67. L("DefaultEmailAddress"), scopes: SettingScopes.Application | SettingScopes.Tenant),
  68. new SettingDefinition(AppSettings.Smtp.DefaultDisplayName, "CompanyName",
  69. L("DefaultDisplayName"), scopes: SettingScopes.Application | SettingScopes.Tenant)
  70. };
  71. }
  72. private static LocalizableString L(string name)
  73. {
  74. return new LocalizableString(name, AbpConsts.LocalizationSourceName);
  75. }
  76. }

2.2 EmailSenderConfiguration配置

这个类的作用如上面提到的那样,主要是读取自定义的AppSettingProvider中设置的邮件参数值

IUserEmailSenderConfiguration接口略

  1. public class UserEmailSenderConfiguration : TruckingServiceBase, IUserEmailSenderConfiguration, ITransientDependency
  2. {
  3. /// <summary>
  4. /// Gets a setting value by checking. Throws <see cref="AbpException"/> if it's null or empty.
  5. /// </summary>
  6. /// <param name="name">Name of the setting</param>
  7. /// <returns>Value of the setting</returns>
  8. protected string GetNotEmptySettingValue(string name)
  9. {
  10. var value = SettingManager.GetSettingValue(name);
  11. if (value.IsNullOrEmpty())
  12. {
  13. throw new AbpException(String.Format("Setting value for '{0}' is null or empty!", name));
  14. }
  15. return value;
  16. }
  17. /// <summary>
  18. /// SMTP Host name/IP.
  19. /// </summary>
  20. public string Host
  21. {
  22. get { return GetNotEmptySettingValue(AppSettings.Smtp.Host); }
  23. }
  24. /// <summary>
  25. /// SMTP Port.
  26. /// </summary>
  27. public int Port
  28. {
  29. get { return SettingManager.GetSettingValue<int>(AppSettings.Smtp.Port); }
  30. }
  31. /// <summary>
  32. /// User name to login to SMTP server.
  33. /// </summary>
  34. public string UserName
  35. {
  36. get { return GetNotEmptySettingValue(AppSettings.Smtp.UserName); }
  37. }
  38. /// <summary>
  39. /// Password to login to SMTP server.
  40. /// </summary>
  41. public string Password
  42. {
  43. get { return GetNotEmptySettingValue(AppSettings.Smtp.Password); }
  44. }
  45. /// <summary>
  46. /// Domain name to login to SMTP server.
  47. /// </summary>
  48. public string Domain
  49. {
  50. get { return SettingManager.GetSettingValue(AppSettings.Smtp.Domain); }
  51. }
  52. /// <summary>
  53. /// Is SSL enabled?
  54. /// </summary>
  55. public bool EnableSsl
  56. {
  57. get { return SettingManager.GetSettingValue<bool>(AppSettings.Smtp.EnableSsl); }
  58. }
  59. /// <summary>
  60. /// Use default credentials?
  61. /// </summary>
  62. public bool UseDefaultCredentials
  63. {
  64. get { return SettingManager.GetSettingValue<bool>(AppSettings.Smtp.UseDefaultCredentials); }
  65. }
  66. public string DefaultAddress
  67. {
  68. get { return GetNotEmptySettingValue(AppSettings.Smtp.DefaultAddress); }
  69. }
  70. public string DefaultDisplayName
  71. {
  72. get { return SettingManager.GetSettingValue(AppSettings.Smtp.DefaultDisplayName); }
  73. }
  74. }

2.3 SmtpEmailSender实现(Smtp实现邮件发送)

UserSmtpEmailSender类才是真正的对Mail操作类,它通过注入IUserEmailSenderConfiguration接口,读取相关的Mail参数,如Host,UserName,Password等,然后再调用.NET的Mail发送邮件。

IUserSmtpEmailSender接口略

  1. public class UserSmtpEmailSender : IUserSmtpEmailSender, ITransientDependency
  2. {
  3. private readonly IUserEmailSenderConfiguration _configuration;
  4. public UserSmtpEmailSender(IUserEmailSenderConfiguration configuration)
  5. {
  6. _configuration = configuration;
  7. }
  8. public async Task SendAsync(string to, string subject, string body, bool isBodyHtml = true)
  9. {
  10. await SendAsync(_configuration.DefaultAddress, to, subject, body, isBodyHtml);
  11. }
  12. public void Send(string to, string subject, string body, bool isBodyHtml = true)
  13. {
  14. Send(_configuration.DefaultAddress, to, subject, body, isBodyHtml);
  15. }
  16. public async Task SendAsync(string from, string to, string subject, string body, bool isBodyHtml = true)
  17. {
  18. await SendAsync(new MailMessage(from, to, subject, body) {IsBodyHtml = isBodyHtml});
  19. }
  20. public void Send(string from, string to, string subject, string body, bool isBodyHtml = true)
  21. {
  22. Send(new MailMessage(from, to, subject, body) {IsBodyHtml = isBodyHtml});
  23. }
  24. public async Task SendAsync(MailMessage mail, bool normalize = true)
  25. {
  26. if (normalize)
  27. NormalizeMail(mail);
  28. await SendEmailAsync(mail);
  29. }
  30. public void Send(MailMessage mail, bool normalize = true)
  31. {
  32. if (normalize)
  33. NormalizeMail(mail);
  34. SendEmail(mail);
  35. }
  36. public SmtpClient BuildClient()
  37. {
  38. var host = _configuration.Host;
  39. var port = _configuration.Port;
  40. var smtpClient = new SmtpClient(host, port);
  41. try
  42. {
  43. if (_configuration.EnableSsl)
  44. smtpClient.EnableSsl = true;
  45. if (_configuration.UseDefaultCredentials)
  46. {
  47. smtpClient.UseDefaultCredentials = true;
  48. }
  49. else
  50. {
  51. smtpClient.UseDefaultCredentials = false;
  52. var userName = _configuration.UserName;
  53. if (!userName.IsNullOrEmpty())
  54. {
  55. var password = _configuration.Password;
  56. var domain = _configuration.Domain;
  57. smtpClient.Credentials = !domain.IsNullOrEmpty()
  58. ? new NetworkCredential(userName, password, domain)
  59. : new NetworkCredential(userName, password);
  60. }
  61. }
  62. return smtpClient;
  63. }
  64. catch
  65. {
  66. smtpClient.Dispose();
  67. throw;
  68. }
  69. }
  70. /// <summary>
  71. /// Normalizes given email.
  72. /// Fills <see cref="MailMessage.From" /> if it's not filled before.
  73. /// Sets encodings to UTF8 if they are not set before.
  74. /// </summary>
  75. /// <param name="mail">Mail to be normalized</param>
  76. protected virtual void NormalizeMail(MailMessage mail)
  77. {
  78. if ((mail.From == null) || mail.From.Address.IsNullOrEmpty())
  79. mail.From = new MailAddress(
  80. _configuration.DefaultAddress,
  81. _configuration.DefaultDisplayName,
  82. Encoding.UTF8
  83. );
  84. if (mail.HeadersEncoding == null)
  85. mail.HeadersEncoding = Encoding.UTF8;
  86. if (mail.SubjectEncoding == null)
  87. mail.SubjectEncoding = Encoding.UTF8;
  88. if (mail.BodyEncoding == null)
  89. mail.BodyEncoding = Encoding.UTF8;
  90. }
  91. protected async Task SendEmailAsync(MailMessage mail)
  92. {
  93. using (var smtpClient = BuildClient())
  94. {
  95. await smtpClient.SendMailAsync(mail);
  96. }
  97. }
  98. protected void SendEmail(MailMessage mail)
  99. {
  100. using (var smtpClient = BuildClient())
  101. {
  102. smtpClient.Send(mail);
  103. }
  104. }
  105. }

之后我们只需要再调用该EmailSender的SendAsync,填入对应的参数,亲测有效。如果之后要更换邮件组件,则只需要实现对应的UserLibraryEmailSerder即可。

至此,我们便将ABP中单独的一个邮件功能抽离了出来并做了相关解释,其实只要花点功夫,自己手动剥离代码图也可以理解了。至于一个简单的邮件功能为什么在ABP中要实现得如此复杂,每个程序员有每个程序员的答案,还是继续学习吧

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注