个人博客-发送邮件优化🧐

前言

之前的发送邮件就弄了个方法,比如回复评论会给评论的人发送邮件,留言回复也是,而且2者的代码有很多一样的地方,比较冗余。然后也是抽空优化一下,思路也是比较常用的工厂+策略模式,然后评论回复和留言回复的模板是不一样的,所以需要创建模板类和方法。。。

内容比较多,可以自己创建个小项目试试~

模板创建

⌨先把模板给定义好

定义EmailContent

主要是用传入发送邮件的内容,Content参数为必须,其他的可以根据自己的需求进行更改。

public class EmailContent
{
    public string Content { get; set; }
    public string? Link { get; set; }
}

public class EmailContent<T>
{
    public string Content { get; set; }
    public string Link { get; set; }
    public T Data { get; set; }
}

首先创建一个基类定义邮件内容生成的方法:

  • EmailTemplate:基本模板类
  • SEmailTemplate:泛型模板类
public abstract class EmailTemplateBase
{
    protected abstract string GetStyle();
}

public abstract class EmailTemplate:EmailTemplateBase
{
    public string GenerateContent(EmailContent emailContent)
    {
        return $"<html>\n<head>\n<style>{GetStyle()}</style>\n</head>\n<body>\n{GetBodyContent(emailContent)}\n</body>\n</html>";
    }
    protected abstract string GetBodyContent(EmailContent emailContent);
}

public abstract class SEmailTemplate<T>:EmailTemplateBase
{
    public string GenerateContent(EmailContent<T> emailContent)
    {
        return $"<html>\n<head>\n{GetStyle()}\n</head>\n<body>\n{GetBodyContent(emailContent)}\n</body>\n</html>";
    }
    
    protected abstract string GetBodyContent(EmailContent<T> emailContent);
}

然后为每一种邮件内容创建一个子类:

情况一:简单情况

只发送简单的邮件内容,不包含对象等复杂内容。

  • GetStyle 里面写样式就行
  • GetBodyContent 里面写内容
public class MessageBoardNotificationEmailTemplate:EmailTemplate
{
    protected override string GetStyle()
    {
        return @"
            /* 添加样式 */
            body {
                font-family: Arial, sans-serif;
                font-size: 14px;
                line-height: 1.5;
                color: #333;
            }
            .box {
                background-color:#90F8FF ;
                border-radius: 5px;
                padding: 20px;
            }
            h1 {
                font-size: 24px;
                font-weight: bold;
                margin-bottom: 20px;
                color: #333;
            }
            p {
                margin-bottom: 10px;
                color: #333;
            }
            a {
                color: #333;
            }";
    }

    protected override string GetBodyContent(EmailContent emailContent)
    {
        return $@"
            <div class='box'>
                <h1>ZY知识库</h1>
                <h3>留言板通知</h3>
                <p>内容如下:{emailContent.Content}</p>
                <p>点击跳转:<a href='https://pljzy.top/MsgBoard?page=1'>留言板地址</a></p>
            </div>";
    }
}

情况二:复杂情况

  • GetStyle 同上
  • GetBodyContent 里面参数为泛型参数EmailContent<LinkExchange>,这样在发送邮件时就能发送对象信息
public class LinksNotificationEmailTemplate:SEmailTemplate<LinkExchange>
{
    protected override string GetStyle()
    {
        return "";
    }

    protected override string GetBodyContent(EmailContent<LinkExchange> emailContent)
    {
        const string blogLink = "<a href=\"https://pljzy.top\">ZY知识库</a>";
        var sb = new StringBuilder();
        sb.AppendLine($"<p>{emailContent.Content}</p>");
        sb.AppendLine($"<br>");
        sb.AppendLine($"<p>以下是您申请的友链信息:</p>");
        sb.AppendLine($"<p>网站名称:{emailContent.Data.Name}</p>");
        sb.AppendLine($"<p>介绍:{emailContent.Data.Description}</p>");
        sb.AppendLine($"<p>网址:{emailContent.Data.Url}</p>");
        sb.AppendLine($"<p>站长:{emailContent.Data.WebMaster}</p>");
        sb.AppendLine($"<p>补充信息:{emailContent.Data.Reason}</p>");
        sb.AppendLine($"<br>");
        sb.AppendLine($"<br>");
        sb.AppendLine($"<br>");
        sb.AppendLine($"<p>本消息由 {blogLink} 自动发送,无需回复。</p>");
        return sb.ToString();
    }
}

发送邮件服务

首先,创建一个接口定义邮件发送服务

public interface IEmailService
{
    Task SendEmail(string recipient, EmailTemplate template, EmailContent emailContent);
    Task SendEmail<T>(string recipient, SEmailTemplate<T> template,  EmailContent<T> emailContent);
}

然后创建实现类MimeKitEmailService ,这里用的是MimeKit库生成邮件内容

public class MimeKitEmailService : IEmailService
{
    private readonly SmtpClient client;

    public MimeKitEmailService(SmtpClient client)
    {
        this.client = client;
    }

    public async Task SendEmail(string recipient, EmailTemplate template, EmailContent emailContent)
    {
        var message = CreateEmailMessage(recipient, template.GenerateContent(emailContent));
        await SendAsync(message);
    }

    public async Task SendEmail<T>(string recipient, SEmailTemplate<T> template, EmailContent<T> emailContent)
    {
        var message = CreateEmailMessage(recipient, template.GenerateContent(emailContent));
        await SendAsync(message);
    }

    private MimeMessage CreateEmailMessage(string recipient, string content)
    {
        var message = new MimeMessage();
        message.From.Add(new MailboxAddress("ZY", "zy1767992919@163.com"));
        message.To.Add(new MailboxAddress("", recipient));
        message.Subject = "来自ZY知识库通知~";
        message.Body = new TextPart("html")
        {
            Text = content
        };

        return message;
    }

    private async Task SendAsync(MimeMessage message)
    {
        await client.SendAsync(message);
        await client.DisconnectAsync(true);
    }
}

创建工厂类

配置

将邮箱信息存放在appsettings.json文件中方便统一管理

{
  "Email": {
    "Address": "你的邮箱",
    "Password": "授权码"
  }
}

然后创建一个类对应上述参数

public class EmailConfig
{
    public string Address { get; set; }
    public string Password { get; set; }
}

Program.cs文件中绑定EmailConfig类,添加到容器中

builder.Services.Configure<EmailConfig>(builder.Configuration.GetSection("Email"));

工厂类

创建EmailServiceFactory工厂类用于实例化EmailService

  1. 网易:smtp.163.com
  2. QQ:smtp.qq.com

EmailServiceFactory类中注入IOptions<EmailConfig>来邮箱信息配置

public class EmailServiceFactory
{
    private readonly EmailConfig emailConfig;
    public EmailServiceFactory(IOptions<EmailConfig> emailConfigOptions)
    {
        emailConfig = emailConfigOptions.Value;
    }
    public async Task<IEmailService> CreateEmailService()
    {
        var client = new SmtpClient();
        // configure the client
        await client.ConnectAsync("smtp.163.com", 465, true);
        await client.AuthenticateAsync(emailConfig.Address, emailConfig.Password);

        return new MimeKitEmailService(client);
    }
}

注册EmailServiceFactory服务

builder.Services.AddTransient<EmailServiceFactory>();

发送邮件

声明 IEmailServiceEmailServiceFactory变量,然后使用 emailServiceFactoryCreateEmailService 方法异步创建一个 IEmailService 对象用于创建邮箱通信,并将其赋给 emailService 变量,然后new一个模板对象,最后调用emailServiceSendEmail方法发送邮件

private IEmailService emailService;
private readonly EmailServiceFactory _emailServiceFactory;

public LinkExchangeService(MyDbContext myDbContext,
        EmailServiceFactory emailServiceFactory)
 {
     _myDbContext = myDbContext;
     _emailServiceFactory = emailServiceFactory;
 }

public async Task SendEmailOnAdd(LinkExchange item)
{
    //创建邮箱连接
    emailService = await _emailServiceFactory.CreateEmailService();
    //内容模板
    var template = new LinksNotificationEmailTemplate(); 
    //发送邮件
    await emailService.SendEmail(item.Email, template,
                                 new EmailContent<LinkExchange>()
                                 {
                                     Content = "您好,友链申请已通过!感谢支持,欢迎互访哦~",
                                     Data = item
                                 });
}

参考资料