前言
网站开发中,经常碰到需要发送邮件的场景。
比如,重置用户密码,需要执行下列流程:
用户在重置页面输入邮箱地址
进入邮箱,使用获得的重置链接打开重置页面
输入新密码
一般来说,重置链接都需要包含一个token
值,用于保证当前重置操作是正确的来源,如下图:
问题
为了将上面的业务实现,示例代码如下:
[ApiController]
[Route("[controller]")]
public class ResetController : ControllerBase
{private readonly Dictionary<string,string> _cache= new Dictionary<string, string>();[HttpGet]public void Get([FromQuery] string email){var token = Guid.NewGuid().ToString("N");_cache.Add(token, email);//发送邮件,smtpServer、from来源于配置using (var smtpClient = new SmtpClient(smtpServer,25)){smtpClient.Send(from, email, "重置密码", token);}}[HttpPost]public string Post(string token, string newPassword){var email = _cache[token];//重置密码逻辑return email;}
}
可以发现,如果要拿到token,我们必须获得邮件内容。
怎么办?
当然,有很多种解决方案,比如从缓存获取token,但是那样会与业务内部实现强耦合。
在这里,我们用Smtp4dev
去真实的获得邮件。
Smtp4dev
Smtp4dev(https://github.com/rnwood/smtp4dev)用于开发和测试的虚拟SMTP电子邮件服务器。可以让你在测试应用程序时,无需向真实客户发送邮件,也无需使用特殊配置设置复杂的真实电子邮件服务器。
安装非常简单,直接执行下列命令:
dotnet tool install -g Rnwood.Smtp4dev
然后执行下列命令运行Smtp4dev:
smtp4dev --urls=https://localhost:9000 --db=d:\codes\smtp4dev.db
--urls
管理页面访问地址--db
邮件存储Sqlite数据库,后面测试会用到
smtp4dev对应的SMTP服务器地址为localhost:25
。
测试用例
测试用例代码很简单:
[Fact]
public void Test1()
{var controller = new ResetController();var email = "user_"+Guid.NewGuid().ToString("N") + "@localhost";controller.Get(email);var token = GetToken(email);var result = controller.Post(token,"111");Assert.Equal(email, result);
}
GetToken
将从邮件中获取token。
获取邮件
虽然smtp4dev提供了API来获取邮件,但是我们这里使用更简单的方式,直接从数据库读取邮件。
首先,创建DbContext:
public class Smtp4devContext : DbContext
{public DbSet<Message> Messages { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){optionsBuilder.UseSqlite(@"DataSource=d:\codes\smtp4dev.db");}
}public class Message
{ public string Id { get; set; }public string To { get; set; }public string ReceivedDate { get; set; }public string Data { get; set; }
}
然后,获取指定收件人的最新邮件:
private static Message GetMessage(string email)
{return new Smtp4devContext().Messages.Where(p => p.To == email).OrderByDescending(p => p.ReceivedDate).First();
}
最后,从邮件的Data
属性中分析出token:
private string GetToken(string email)
{var message = GetMessage(email);var lines = message.Data.Split("\r\n", StringSplitOptions.RemoveEmptyEntries);return lines.Last();
}
运行测试,成功!
结论
如果你在开发和测试时需要发送邮件,试试Smtp4dev吧!
如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我!