基于Razor(.cshtml)模板的.NET Core 2.0邮件发送类库问询
我太懂你这种需求了——找了好久都没找到清晰的方案,想用Razor视图当邮件模板,还想打包成类库让别的Web应用直接引用实现发邮件,对吧?我之前折腾过这个,给你分享一套落地的步骤:
1. 先搭好Razor类库的基础结构
别用普通的类库,直接创建Razor类库(Razor Class Library),选“支持页面和视图”的模板——这种项目天生就支持Razor视图的编译与嵌入,完美适配你的需求。
2. 定义邮件模型与模板
- 新建
Models文件夹,放对应模板的模型类,比如PasswordResetEmailModel,把邮件需要的字段都塞进去:用户名、重置链接、过期时间这些。 - 新建
Views/Emails文件夹,把你的Razor模板放进去,比如PasswordReset.cshtml。顶部记得指定模型:@model YourLibraryName.Models.PasswordResetEmailModel <!DOCTYPE html> <html> <head> <title>密码重置</title> <style> /* 邮件里尽量用内联样式,避免客户端不识别外部CSS */ .container { padding: 20px; background-color: #f5f5f5; } .content { background-color: white; padding: 30px; border-radius: 8px; } </style> </head> <body> <div class="container"> <div class="content"> <h3>您好,@Model.UserName</h3> <p>请点击下方链接重置您的密码:</p> <a href="@Model.ResetLink">重置密码</a> <p>链接将在@Model.ExpireTime 后失效</p> </div> </div> </body> </html>
3. 实现EmailSender服务
在类库新建Services文件夹,先搞个接口IEmailSender,再写实现类EmailSender:
- 核心是把Razor视图渲染成HTML字符串,需要注入
IRazorViewEngine、ITempDataProvider、IHttpContextAccessor这些服务(如果类库没有这些依赖,要先引用Microsoft.AspNetCore.Mvc.ViewFeatures包)。 - 写一个
RenderViewToStringAsync方法,负责视图渲染:private async Task<string> RenderViewToStringAsync(string viewPath, object model) { var actionContext = new ActionContext( _httpContextAccessor.HttpContext ?? new DefaultHttpContext(), new RouteData(), new ActionDescriptor() ); using var sw = new StringWriter(); var viewResult = _razorViewEngine.FindView(actionContext, viewPath, false); if (!viewResult.Success) { throw new InvalidOperationException($"找不到视图:{viewPath}"); } var viewDictionary = new ViewDataDictionary( new EmptyModelMetadataProvider(), new ModelStateDictionary() ) { Model = model }; var viewContext = new ViewContext( actionContext, viewResult.View, viewDictionary, new TempDataDictionary(actionContext.HttpContext, _tempDataProvider), sw, new HtmlHelperOptions() ); await viewResult.View.RenderAsync(viewContext); return sw.ToString(); } - 然后在
SendEmailAsync方法里,先渲染HTML,再用邮件客户端发送(推荐用MailKit,比.NET自带的SmtpClient靠谱):public async Task SendEmailAsync(string toEmail, string subject, object model, string viewPath) { var htmlContent = await RenderViewToStringAsync(viewPath, model); // 这里用MailKit实现发送逻辑,需要先引用MailKit和MimeKit包 var message = new MimeMessage(); message.From.Add(new MailboxAddress("你的发件人名称", "sender@example.com")); message.To.Add(new MailboxAddress("", toEmail)); message.Subject = subject; message.Body = new TextPart("html") { Text = htmlContent }; using var client = new SmtpClient(); await client.ConnectAsync("smtp.example.com", 587, SecureSocketOptions.StartTls); await client.AuthenticateAsync("your-username", "your-password"); await client.SendAsync(message); await client.DisconnectAsync(true); }
4. 确保视图被正确嵌入到类库
打开类库的.csproj文件,确认有这段配置(没有的话手动加上),这样所有.cshtml文件都会被嵌入到DLL里,引用类库的Web应用不需要复制视图文件:
<ItemGroup> <EmbeddedResource Include="Views/**/*.cshtml" /> </ItemGroup>
5. Web应用引用与使用
- 把类库作为项目引用或者NuGet包添加到你的Web应用。
- 在Web应用的
Program.cs里注册服务:builder.Services.AddScoped<IEmailSender, YourLibraryName.Services.EmailSender>(); // 别忘了注册IHttpContextAccessor,因为渲染视图需要它 builder.Services.AddHttpContextAccessor(); - 然后在需要发邮件的地方(比如控制器、服务)注入
IEmailSender,调用方法就行:var model = new PasswordResetEmailModel { UserName = "张三", ResetLink = "https://yourapp.com/reset-password?token=xxx", ExpireTime = DateTime.Now.AddHours(1).ToString("yyyy-MM-dd HH:mm:ss") }; await _emailSender.SendEmailAsync("user@example.com", "密码重置通知", model, "/Views/Emails/PasswordReset.cshtml");
几个关键提醒
- 邮件里的样式尽量用内联CSS,大部分邮件客户端不支持外部样式表。
- 邮件服务器的配置(SMTP地址、账号、密码)可以让Web应用通过
appsettings.json配置,在EmailSender里注入IConfiguration读取,不要硬编码在类库中。 - 测试的时候,可以先把渲染后的HTML保存到本地文件,确认格式没问题再实际发送,避免频繁发测试邮件。
内容的提问来源于stack exchange,提问作者akd
相关产品推荐
相关产品推荐

