使用 SendGrid 實作 IEmailSender 替換服務
結論
Domain 專案
安裝 SendGrid.Extensions.DependencyInjection
Host (Web) 專案
設定 API KEY
appsetting.json
"SendGrid": {
"ApiKey": "SG.xxxxxxxxxxx"
}
設定 SendGrid 服務
SendGridTestWebModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var configuration = context.Services.GetConfiguration();
ConfigureAuthentication(context);
ConfigureUrls(configuration);
ConfigureBundles();
ConfigureAutoMapper();
ConfigureVirtualFileSystem(hostingEnvironment);
ConfigureNavigationServices();
ConfigureAutoApiControllers();
ConfigureSwaggerServices(context.Services);
// 設定 key
context.Services.AddSendGrid(options =>
{
options.ApiKey = configuration["SendGrid:ApiKey"];
});
}
到這邊是原本 SendGrid 初始化的部分
此時可以直接用 SendGrid 寄信了
接著是透過 ABP 的 IEmailSender
寄信的部分
好處是可以隨時替換寄信方式
例如內建 SMTP 或是 MailKit … ETC.
Domain 專案
ISendGridEmailSender
public interface ISendGridEmailSender : IEmailSender
{
}
SendGridEmailSender
using System;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using SendGrid;
using SendGrid.Helpers.Mail;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;
using Attachment = SendGrid.Helpers.Mail.Attachment;
namespace SendGridTest.SendGrid;
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
public class SendGridEmailSender : EmailSenderBase, ISendGridEmailSender
{
private readonly ISendGridClient _sendGridClient;
public SendGridEmailSender(
IEmailSenderConfiguration configuration,
IBackgroundJobManager backgroundJobManager,
ISendGridClient sendGridClient)
: base(configuration, backgroundJobManager)
{
_sendGridClient = sendGridClient;
}
protected override async Task SendEmailAsync(MailMessage mail)
{
var msg = new SendGridMessage()
{
From = new EmailAddress(mail.From?.Address, mail.From?.DisplayName),
Subject = mail.Subject
};
if (mail.IsBodyHtml)
{
msg.HtmlContent = mail.Body;
}
else
{
msg.PlainTextContent = mail.Body;
}
mail.To.ToList().ForEach(t => msg.AddTo(new EmailAddress(t.Address, t.DisplayName)));
mail.CC.ToList().ForEach(t => msg.AddCc(new EmailAddress(t.Address, t.DisplayName)));
mail.Bcc.ToList().ForEach(t => msg.AddBcc(new EmailAddress(t.Address, t.DisplayName)));
if (mail.Attachments.Any())
{
msg.Attachments = mail.Attachments.Select(t => new Attachment
{
Content = Convert.ToBase64String(t.ContentStream.GetAllBytes()),
Filename = t.Name,
Type = t.ContentType.MediaType,
Disposition = "attachment"
}).ToList();
}
var response = await _sendGridClient.SendEmailAsync(msg);
if (response.StatusCode != System.Net.HttpStatusCode.Accepted)
{
var result = await response.Body.ReadAsStringAsync();
throw new Exception(result);
}
}
}
至此就可以用 IEmailSender
寄信了
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Emailing;
namespace SendGridTest.SendGrid;
public class SendGridAppService : SendGridTestAppService
{
private readonly IEmailSender _emailSender;
public SendGridAppService(IEmailSender emailSender)
{
_emailSender = emailSender;
}
public async Task GetAsync(CancellationToken cancellationToken)
{
await _emailSender.SendAsync("jakeuj@hotmail.com", "test", "test");
}
}
但如果要在本機開發測試需要注意一下
SendGridTestDomainModule
#if DEBUG
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
#endif
需要把這行註解掉,不然會寄不出去
測試
同上需要註解掉 NullEmailSender
SendGridTest.Domain.Tests > SampleDomainTests
[Fact]
public async Task Should_Get_Email_Of_A_User()
{
var emailSender = GetRequiredService<IEmailSender>();
using var stream = new MemoryStream();
await stream.WriteAsync(Encoding.Default.GetBytes("Test"));
stream.Position = 0;
var mailMessage = new MailMessage("jakeuj@hotmail.com", "jakeuj@hotmail.com","test", "test")
{
//Stream contentStream, string? name, string? mediaType
Attachments = { new Attachment(stream,"test.txt") }
};
await WithUnitOfWorkAsync(async () =>
{
await emailSender.SendAsync(mailMessage);
});
emailSender.ShouldNotBeNull();
}
SendGridTest.TestBase > SendGridTestTestBaseModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBackgroundJobOptions>(options =>
{
options.IsJobExecutionEnabled = false;
});
context.Services.AddAlwaysAllowAuthorization();
context.Services.AddSendGrid(options =>
{
options.ApiKey = "SG.xxxxxxxxxxxxxxxxxx";
});
}
這樣就可以發信,並且收到帶有附件的測試信,打開裡面應該是 Test