ASP.NET Core 工厂模式+策略模式管理多类型邮件内容实战

记录使用工厂模式加策略模式解决有多个不同邮件内容的难管理的痛点

那天我正在写一个发送邮件验证码的功能,但是我开始的时候一出手就是if else,这样来判断应该发送什么内容的邮件,
后来我发现这样写太难维护了,然后我又换成了switch case,这样来判断应该发送什么内容的邮件,但是我觉得写太普通了,
所以我又换成了工厂模式加策略模式,这样来判断应该发送什么内容的邮件,这样就很方便了,而且也很好维护。后面需要新增邮件内容的时候,
只需要新增一个策略类,然后在工厂类的字典里面添加新的映射就行。所以我感觉设计模式还是很重要的,让代码更容易维护。不然如果使用if else,
那就需要修改很多地方。

项目文件

appsettings.json
IEmailStrategy.cs
IEmailStrategyFactory.cs
IMailSendService.cs
EmailStrategyFactory.cs
MailSendService.cs
CaptchaConfig.cs // 验证码配置
MailSendServiceConfig.cs // 邮件发送服务配置

appsettings.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": ""
},
"MailSendServiceConfig": {
"Host": "smtp.qq.com",
"Port": 587,
"EmailLocalAddress": "",
"keyword": ""
},
"SessionConfigure": {
"ExpirationTime": "3"
},
"CaptchaConfig": {
"CaptchaExpirationTime": "5"
},
"AllowedHosts": "*"
}

IEmailStrategy.cs

邮箱策略接口
后面如果需要新增邮件类型,只需要新增一个策略类,然后在工厂类的字典里面添加新的映射就行

1
2
3
4
5
6
7
8
9
namespace DaiBanShiWuList01x02.Interfaces
{
public interface IEmailStrategy
{
string Subject { get; }
string BuildBody(string captchaSTR, string effectiveTime);
}
}

具体邮箱策略:AccountLoginEmailStrategy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
using DaiBanShiWuList01x02.Interfaces;

namespace DaiBanShiWuList01x02.Services.EmailStrategys
{
public class AccountLoginEmailStrategy: IEmailStrategy
{
public string Subject => "ClipFlow登录验证码";
public string BuildBody(string captchaSTR, string effectiveTime) => $@"
<html>
<head>
<meta charset=""UTF-8"">
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
<title>ClipFlow 验证码邮件</title>
<style>
body {{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}}
.email-container {{
max-width: 600px;
margin: 0 auto;
background-color: #ffffff;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
overflow: hidden;
}}
.email-header {{
background-color: #2c3e50;
color: white;
padding: 20px;
text-align: center;
}}
.email-body {{
padding: 30px;
}}
.email-footer {{
padding: 20px;
text-align: center;
font-size: 12px;
color: #777;
border-top: 1px solid #eee;
background-color: #f9f9f9;
}}
.captcha-container {{
margin: 20px 0;
text-align: center;
}}
.note {{
font-size: 14px;
color: #777;
margin-top: 20px;
}}
.divider {{
height: 1px;
background-color: #eee;
margin: 20px 0;
}}
</style>
</head>
<body>
<div class=""email-container"">
<div class=""email-header"">
<h1>ClipFlow</h1>
</div>
<div class=""email-body"">
<h3>尊敬的用户,您好!</h3>
<p>您正在进行 ClipFlow 登录操作,请使用以下验证码完成登录:</p>

<div class=""captcha-container"">
<span style='color:rgb(34, 9, 218);font-size:24px;'>{captchaSTR}</span>
</div>

<div class=""note"">
<p>此验证码 {effectiveTime} 分钟内有效,请勿将验证码透露给他人。</p>
</div>
</div>

<div class=""divider""></div>

<div class=""email-footer"">
<p>此为系统邮件,请勿回复</p>
<p>© 2025 ClipFlow 团队 版权所有</p>
</div>
</div>
</body>
</html>";

}
}

具体邮箱策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
using DaiBanShiWuList01x02.Interfaces;
namespace DaiBanShiWuList01x02.Services.EmailStrategys
{
public class AccountRegisterEmailStrategy:IEmailStrategy
{
public string Subject => "ClipFlow注册验证码";
public string BuildBody(string captchaSTR, string effectiveTime) => $@"
<html>
<head>
<meta charset=""UTF-8"">
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
<title>ClipFlow 验证码邮件</title>
<style>
body {{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}}
.email-container {{
max-width: 600px;
margin: 0 auto;
background-color: #ffffff;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
overflow: hidden;
}}
.email-header {{
background-color: #2c3e50;
color: white;
padding: 20px;
text-align: center;
}}
.email-body {{
padding: 30px;
}}
.email-footer {{
padding: 20px;
text-align: center;
font-size: 12px;
color: #777;
border-top: 1px solid #eee;
background-color: #f9f9f9;
}}
.captcha-container {{
margin: 20px 0;
text-align: center;
}}
.note {{
font-size: 14px;
color: #777;
margin-top: 20px;
}}
.divider {{
height: 1px;
background-color: #eee;
margin: 20px 0;
}}
</style>
</head>
<body>
<div class=""email-container"">
<div class=""email-header"">
<h1>ClipFlow</h1>
</div>
<div class=""email-body"">
<h3>尊敬的用户,您好!</h3>
<p>您正在进行 ClipFlow 注册操作,请使用以下验证码完成验证:</p>

<div class=""captcha-container"">
<span style='color:rgb(34, 9, 218);font-size:24px;'>{captchaSTR}</span>
</div>

<div class=""note"">
<p>此验证码 {effectiveTime} 分钟内有效,请勿将验证码透露给他人。</p>
</div>
</div>

<div class=""divider""></div>

<div class=""email-footer"">
<p>此为系统邮件,请勿回复</p>
<p>© 2025 ClipFlow 团队 版权所有</p>
</div>
</div>
</body>
</html>";
}
}

IEmailStrategyFactory.cs

邮箱策略工厂接口

1
2
3
4
5
6
7
8
namespace DaiBanShiWuList01x02.Interfaces
{
public interface IEmailStrategyFactory
{
IEmailStrategy GetEmailStrategy(string emailType);
}
}

注意工厂里面的邮箱策略我的文章中仅实现了两个

如果以后要实现新的邮件策略的话,就新增一个策略类继承策略接口,然后在策略工厂中注册就行了。这种添加新功能,只需要创建新的类的方式,对于拓展和管理都非常方便。
我觉得下一篇文章我可以讲一下通用仓储接口的设计(其实就是一个仓储层,我觉得这个东西非常有意思)。

EmailStrategyFactory.cs

邮箱策略工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using DaiBanShiWuList01x02.Interfaces;
using DaiBanShiWuList01x02.Services.EmailStrategys;
namespace DaiBanShiWuList01x02.Services
{
public class EmailStrategyFactory: IEmailStrategyFactory
{
private readonly Dictionary<string, IEmailStrategy> _emailStrategies;

public EmailStrategyFactory()
{
// 目前是字典映射,后面会改成通过配置文件添加,2025-10-10
//后面可以在加新的邮件类型,即把待办事项列表通过邮件发送给用户,提醒用户有哪些待办事项,2025-11-28
//这种情况策略模式的好处就体现出来了,只需要新增一个策略类,然后在这里注册即可2025-11-28
_emailStrategies = new Dictionary<string, IEmailStrategy>
{
["AccountRegister"] = new AccountRegisterEmailStrategy(),
["PasswordForget"] = new PasswordForgetEmailStrategy(),
["FriendVerification"] = new FriendVerificationEmailStrategy(),
["AccountLogin"] = new AccountLoginEmailStrategy(),//2025-12-01,新增邮箱验证码登录
["ChangeAccountName"] = new ChangeAccountNameEmailStrategy()//2025-12-02,修改账号名称
};
}

public IEmailStrategy GetEmailStrategy(string emailType)
{
_emailStrategies.TryGetValue(emailType, out var strategy);
return strategy;
}
}
}

IMailSendService.cs

邮件发送服务接口

1
2
3
4
5
6
7
8
9
namespace DaiBanShiWuList01x02.Interfaces
{
public interface IMailSendService
{
//emailType:AccountRegister,PasswordForget,FriendVerification该参数用于策略模式选择不同的邮件模板,2025-10-4
void SendMailSendService(string SendToMailAdd, string captchaSTR, string emailType);
}
}

MailSendService.cs

邮件发送服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using DaiBanShiWuList01x02.Interfaces;
using Microsoft.Extensions.Options;
using System.Net.Mail;
using System.Net;
namespace DaiBanShiWuList01x02.Services
{
public class MailSendService: IMailSendService
{

//使用策略模式处理不同类型的邮件,相关代码已转移到文件夹Services/EmailStrategys,2025-10-4

private readonly IOptionsMonitor<MailSendServiceConfig> _mailSendServiceConfig;
private readonly IOptionsMonitor<CaptchaConfig> _captchaConfig;//2025-12-02
private readonly IEmailStrategyFactory _emailStrategyFactory;//这里我改了,原来是用的IEmailStrategyFactory,现在直接用工厂,2025-11-28,又改为使用接口
public MailSendService(IOptionsMonitor<MailSendServiceConfig> mailSendServiceConfig, IEmailStrategyFactory emailStrategyFactory, IOptionsMonitor<CaptchaConfig> captchaConfig)
{
_mailSendServiceConfig = mailSendServiceConfig;
_emailStrategyFactory = emailStrategyFactory;
_captchaConfig = captchaConfig;
}

public void SendMailSendService(string SendToMailAdd, string captchaSTR, string emailType)//向该邮箱(SendToMailAdd)发送验证码(captchaSTR)
{
//目前已有类型(3个,注意为字符串)
//"AccountRegister"
//"PasswordForget"
//"FriendVerification"
//"AccountLogin"//2025-12-01,新增邮箱验证码登录
var emailStrategy = _emailStrategyFactory.GetEmailStrategy(emailType);//2025-10-4,策略待实现。new 已经实现2025-10-26

MailMessage mailMessage = new MailMessage
{
From = new MailAddress(_mailSendServiceConfig.CurrentValue.EmailLocalAddress, "ClipFlow", System.Text.Encoding.UTF8),
To = { new MailAddress(SendToMailAdd) },
Bcc = { new MailAddress(SendToMailAdd) },
Subject = emailStrategy.Subject,
SubjectEncoding = System.Text.Encoding.UTF8,
IsBodyHtml = true,
Body = $@"{emailStrategy.BuildBody(captchaSTR,_captchaConfig.CurrentValue.CaptchaExpirationTime)}",
BodyEncoding = System.Text.Encoding.UTF8
};

SmtpClient smtpClient = new SmtpClient(_mailSendServiceConfig.CurrentValue.Host, _mailSendServiceConfig.CurrentValue.Port);
smtpClient.EnableSsl = true;
smtpClient.Credentials = new NetworkCredential(_mailSendServiceConfig.CurrentValue.EmailLocalAddress, _mailSendServiceConfig.CurrentValue.keyword);
try
{
smtpClient.Send(mailMessage);
}
catch (Exception ex)
{
Console.WriteLine($"邮件发送失败: {ex.Message}");
throw;
}
}
}
}

CaptchaConfig.cs

验证码配置

1
2
3
4
5
6
7
8
namespace DaiBanShiWuList01x02
{
public class CaptchaConfig
{
public string CaptchaExpirationTime { get; set; }
}
}

MailSendServiceConfig.cs

邮件发送服务配置

1
2
3
4
5
6
7
8
9
10
11
namespace DaiBanShiWuList01x02
{
public class MailSendServiceConfig
{
public string Host { get; set; }
public int Port { get; set; }
public string EmailLocalAddress { get; set; }
public string keyword { get; set; }
}
}

使用

在控制器里面声明属性:

private readonly IMailSendService _mailSendService;
然后在构造函数中注入:

1
2
3
4
public HomeController(IMailSendService mailSendService)
{
_mailSendService = mailSendService;
}

在控制器方法中使用:

1
_mailSendService.SendMailSendService(userAccount.Email, loginCaptchaSTR, "AccountLogin");