用途
MJML Email Templates 用于在 .NET 项目中构建响应式邮件模板。它适合以下场景:
- 构建事务性邮件(注册、密码重置、发票、通知等)
- 创建可在各邮件客户端中正常显示的响应式邮件模板
- 在 .NET 中设置 MJML 模板渲染
相关技能:aspire/mailpit-integration(使用 Mailpit 在本地测试邮件)、testing/verify-email-snapshots(对渲染后的 HTML 进行快照测试)。
为什么使用 MJML
邮件 HTML 的编写以困难著称:每个邮件客户端(Outlook、Gmail、Apple Mail)的渲染方式不同,需要复杂的基于表格的布局和内联样式。MJML 是一种标记语言,可编译为响应式、跨客户端的 HTML。例如一段简单的 MJML:
<mj-section>
<mj-column>
<mj-text>Hello {{UserName}}</mj-text>
<mj-button href="{{ActionUrl}}">Click Here</mj-button>
</mj-column>
</mj-section>
它会编译为约 200 行基于表格、带内联样式的 HTML,可在各处正常显示。
安装
添加 Mjml.Net 包:
dotnet add package Mjml.Net
在 .csproj 中将模板嵌入为资源:
<ItemGroup>
<EmbeddedResource Include="Templates\**\*.mjml" />
</ItemGroup>
项目结构
示例结构如下:
src/
Infrastructure/
MyApp.Infrastructure.Mailing/
Templates/
_Layout.mjml # 共享布局(页眉、页脚)
UserInvitations/
UserSignupInvitation.mjml
InvitationExpired.mjml
PasswordReset/
PasswordReset.mjml
Billing/
PaymentReceipt.mjml
RenewalReminder.mjml
Mjml/
IMjmlTemplateRenderer.cs
MjmlTemplateRenderer.cs
MjmlEmailMessage.cs
Composers/
IUserEmailComposer.cs
UserEmailComposer.cs
MyApp.Infrastructure.Mailing.csproj
布局模板
_Layout.mjml 定义共享的页眉、页脚、全局属性与样式,并通过 {{Content}} 占位符注入具体内容。示例包含 mj-head(标题、预览文本、全局属性、内联样式)、mj-body(页眉 logo、内容区、页脚版权信息)。
内容模板
内容模板只包含正文片段,会被自动包裹进 _Layout.mjml。示例 UserInvitations/UserSignupInvitation.mjml 使用 mj-text、mj-button 等组件,并包含 {{OrganizationName}}、{{InviteeName}}、{{InviterName}}、{{InvitationLink}}、{{ExpirationDate}} 等变量。
模板渲染器
IMjmlTemplateRenderer 接口定义 RenderTemplateAsync 方法,接收模板名、变量字典和取消令牌。MjmlTemplateRenderer 实现类负责:加载内容模板、加载布局并注入内容、合并变量(布局变量与模板变量)、替换变量、使用 MjmlRenderer 编译为 HTML,并在编译出错时抛出异常。模板通过程序集清单资源加载,变量替换使用正则 \{\{([^}]+)\}\}。
邮件组装器模式
通过强类型值对象将模板渲染与邮件组装分离。IUserEmailComposer 定义 ComposeSignupInvitationAsync 等方法,接收 EmailAddress、PersonName、OrganizationName、AbsoluteUri、DateTimeOffset 等强类型参数。UserEmailComposer 构造变量字典、调用渲染器并返回 EmailMessage(包含收件人、主题、HTML 正文)。
邮件预览端点
可添加管理员端点用于开发期预览邮件:
app.MapGet("/admin/emails/preview/{template}", async (
string template,
IMjmlTemplateRenderer renderer) =>
{
var sampleVariables = GetSampleVariables(template);
var html = await renderer.RenderTemplateAsync(template, sampleVariables);
return Results.Content(html, "text/html");
})
.RequireAuthorization("AdminOnly");
最佳实践
模板设计:使用 MJML 组件进行布局(如 mj-section + mj-column + mj-text),不要使用原始 HTML 表格;图片使用生产环境 URL(如 https://myapp.com/logo.png),不要使用相对路径。
变量处理:使用强类型值对象(如 EmailAddress、PersonName、AbsoluteUri),不要使用原始字符串。
MJML 组件参考
<mj-section>:水平容器(类似行)<mj-column>:section 内的垂直容器<mj-text>:带样式的文本内容<mj-button>:行动号召按钮<mj-image>:响应式图片<mj-divider>:水平分隔线<mj-spacer>:垂直间距<mj-table>:数据表格<mj-social>:社交媒体图标
资源
- MJML 文档:https://documentation.mjml.io/
- MJML Playground:https://mjml.io/try-it-live
- Mjml.Net:https://github.com/ArtZab/Mjml.Net
限制
资料未提供该技能的具体收费、价格、平台支持范围或性能数据,这些信息暂未提供。