这篇文章是面向希望充分利用SparkPost 的电子邮件模板功能的开发者。假设您已熟悉阅读 JSON 内容并遵循基本的编程流程。引入了可能对您来说是新的术语,例如RFC 5322,文本链接到其源参考。说完这些,让我们直接开始吧。
SparkPost 的模板和传输功能简化了发送电子邮件的过程。这些功能为文本和HTML内容提供了抽象层,这意味着大多数情况下无需直接编码由RFC 5322定义的原始电子邮件格式,该格式以前称为RFC 822。但有时您可能希望创建更复杂的消息,这些消息包含通过 SparkPost 的 RESTful 接口未直接暴露的多用途互联网邮件扩展(MIME)部分。
简化的电子邮件撰写
首先,让我们回顾一下发送电子邮件的理想情况。使用传输端点提供text
和HTML内容。在幕后,SparkPost 负责撰写有效的 RFC 5322 电子邮件。SparkPost 将从substitution_data中插入替代变量到文本和 HTML 内容中。这是一种强大的方法,可以为通用模板中的每个收件人生成自定义内容。
以下是使用替代数据进行 HTML 和文本内容的传输示例。
{
"options": {
"open_tracking": true,
"click_tracking": true
},
"campaign_id": "christmas_campaign",
"return_path": "bounces-christmas-campaign@domain.com",
"metadata": {
"user_type": "students"
},
"substitution_data": {
"sender": "Big Store Team"
},
"recipients": [
{
"return_path": "123@bounces.domain.com",
"address": {
"email": "wilma@domain.com",
"name": "Wilma Flintstone"
},
"tags": [
"greeting",
"prehistoric",
"fred",
"flintstone"
],
"metadata": {
"place": "Bedrock"
},
"substitution_data": {
"customer_type": "Platinum"
}
}
],
"content": {
"from": {
"name": "Fred Flintstone",
"email": "fred@domain.com"
},
"subject": "Big Christmas savings!",
"reply_to": "Christmas Sales <sales@domain.com>",
"headers": {
"X-Customer-Campaign-ID": "christmas_campaign"
},
"text": "Hi {{address.name}} \nSave big this Christmas in your area {{place}}! \nClick http://www.mysite.com and get huge discount\n Hurry, this offer is only to {{user_type}}\n {{sender}}",
"html": "<p>Hi {{address.name}} <br>Save big this Christmas in your area {{place}}! <br>Click <a href=\"http://www.mysite.com\">here</a> and get huge discount</p><p>Hurry, this offer is only to {{user_type}}</p><p>{{sender}}</p>"
}
}
替换数据数组
许多人发现 SparkPost 的传输和模板端点可以在电子邮件头和正文中进行简单的内容替换。但很多人忽略了提供条件内容或数据数组的能力,也可以进行替换。您还可以为每个收件人提供独特的内容。在此示例中,我们为每个收件人发送一组唯一的链接。
这是通过提供一个将被填充到电子邮件正文的 JSON 数据数组来实现的。一旦提供数据,SparkPost 将使用模板中的逻辑来填充它。
在此示例中,SparkPost 将查找名为“files_html”的替代数据,并对数组中的每个元素执行“for each”。它将在“files_html”元素中创建一个值为“file”的行。请注意“loop_var.file”周围的三重大括号。这是因为数组的每个元素都包含 HTML,我们需要告诉服务器将其照原样使用,而不是转义它。文本部分将是一个简单的文本标签和文件的 URL。
<table>
{{each files_html}}
<tr>
<td>{{{loop_var.file}}}</td>
</tr>
{{ end }}
</table>
以下是完整的工作示例:
{
"recipients": [
{
"address": {
"email": "recipient1@domain.com"
},
"substitution_data": {
"files_html": [
{
"file": "<a href=\"http://domain.com/file1a.txt\">File 1a Description</a>"
},
{
"file": "<a href=\"http://domain.com/file2a.txt\">File 2a Description</a>"
}
],
"files_plain": [
{
"file": "File 1a -- http://domain.com/file1a.txt"
},
{
"file": "File 2a -- http://domain.com/file2a.txt"
}
]
}
},
{
"address": {
"email": "recipient2@domain.com"
},
"substitution_data": {
"files_html": [
{
"file": "<a href=\"http://domain.com/file1b.txt\">File 1b Description</a>"
},
{
"file": "<a href=\"http://domain.com/file2b.txt\">File 2b Description</a>"
}
],
"files_plain": [
{
"file": "File 1b -- http://domain.com/file1b.txt"
},
{
"file": "File 2b -- http://domain.com/file2b.txt"
}
]
}
}
],
"return_path": "chris@test.domain.com",
"content": {
"from": {
"name": "chris@test.domain.com",
"email": "chris@test.domain.com"
},
"subject": "Sending with SparkPost is Fun",
"html": "<b>Your Files:</b><br>\n<table>\n {{each files_html}}\n <tr><td>{{{loop_var.file}}}</td></tr>\n {{ end }}\n</table>\n",
"text": "Your Files:\n{{each files_plain}} {{loop_var.file}}\n{{ end }}\n"
}
}
专业提示:在您的代码中,建议将视图标记与数据分开,但这里的目标是尽可能简单并易于理解,因此我们创建了两个数组。一个数组用于 HTML 部分,另一个用于文本部分。在生产使用中,通常只有一组数据,并在模板代码中编写逻辑。
传输功能中的附件
传输端点还提供了发送附件的抽象。如下所示,附件在content.attachments数组中指定,其中数组中的每个对象描述了一个单独的附件项目。就像之前一样,SparkPost 将处理文本、HTML、替换和迭代附件数组以编码一个格式正确的电子邮件消息。
最佳实践规定,除非您的服务明确要求,否则最好避免发送附件。
附件的必需字段如下:
type:附件的 MIME 类型
name:附件的文件名
data:Base64编码的文件数据
这是传输内容节点中的附件示例:
"content": {
"attachments": [
{
"type": "audio/mp3",
"name": "voicemail.mp3",
"data": "TVAzIERhdGEK"
}
]
}
您还可以在传输中发送“内嵌图像”。这些图像与附件非常相似,在content.inline_images数组中指定,inline_image对象与上面显示的附件对象类似。
模板中的附件
现在我们已经了解了使用传输端点发送附件的正确背景,让我们看看如何通过模板实现这一点。在撰写本文时,没有像内联传输所需的一样的附件抽象。有人可能会得出模板无法创建附件的结论。您部分正确,但有一种变通方法,尽管您将不再被RFC 5322格式所隔离。
您可以通过自己编码 RFC 5322 内容(包括附件)来实现模板中的附件。好消息是您不会失去在电子邮件头、HTML和文本部分中使用替换数据的能力。请注意,这种类型的模板将替换限制为头和第一个HTML及第一个文本部分。
以下是实现方法的示例。
RFC822 电子邮件
创建具有您想要的替换数据的 RFC 5322 电子邮件。我在我的邮件客户端中创建了这个,并发送给我自己。一旦收到它,我复制源代码并替换我想动态替换的字段。
MIME-Version: 1.0
Reply-To: {{replyto}}
Subject: {{subject}}
From: {{from}}
To: {{address.email}}
Content-Type: multipart/mixed; boundary="001a113c48b0b89d92052d3051da"
--001a113c48b0b89d92052d3051da
Content-Type: multipart/alternative; boundary="001a113c48b0b89d89052d3051d8"
--001a113c48b0b89d89052d3051d8
Content-Type: text/plain; charset=UTF-8
Email with a *text attachment*.
{{body2}}
--001a113c48b0b89d89052d3051d8
Content-Type: text/html; charset=UTF-8
<div dir="ltr">
<div>Email with a <i>text attachment</i>.</div>
{{body1}}
<
在此消息中的最后一个 MIME 部分,您将看到Content-Disposition: attachment; filename=myfile.txt”。这就是文件名的定义位置。您的附件内容肯定会更加复杂,但此示例试图保持简单。
存储的模板
一旦您拥有有效的 RFC 5322 电子邮件,就使用模板端点的email_rfc822形式而不是text和HTML字段来存储它。以下是该消息的content的示例:
{
"content": {
"email_rfc822": "MIME-Version: 1.0\nReply-To: {{replyto}}\nSubject: {{subject}}\nFrom: {{from}}\nTo: {{address.email}}\nContent-Type: multipart/mixed; boundary=001a113c48b0b89d92052d3051da\n\n--001a113c48b0b89d92052d3051da\nContent-Type: multipart/alternative; boundary=001a113c48b0b89d89052d3051d8\n\n--001a113c48b0b89d89052d3051d8\nContent-Type: text/plain; charset=UTF-8\n\nEmail with a *text attachment*.\n\n{{body2}}\n\n--001a113c48b0b89d89052d3051d8\nContent-Type: text/html; charset=UTF-8\n\n<div dir=\"ltr\"><div>Email with a <i>text attachment</i>.</div>\n\n{{body1}}\n</div>\n\n--001a113c48b0b89d89052d3051d8--\n--001a113c48b0b89d92052d3051da\nContent-Type: text/plain; charset=US-ASCII; name=\"myfile.txt\"\nContent-Disposition: attachment; filename=\"myfile.txt\"\nContent-Transfer-Encoding: base64\nX-Attachment-Id: f_ild455ce0\n\nVGhpcyBpcyBteSBzaW1wbGUgdGV4dCBmaWxlLgo=\n--001a113c48b0b89d92052d3051da--"
},
"name": "_TMP_TEMPLATE_TEST"
}
请求完成后,SparkPost 将为您的新模板响应一个唯一标识符。例如xxxxxxx。
发送模板
好消息是,创建 RFC 5322 内容是最困难的部分。从此以后,使用 SparkPost 发送模板与发送任何其他模板完全相同。
下面是我们如何发送该模板并填充替换数据:
{
"campaign_id": "MyCampaign",
"return_path": "myReturnPath@yourdomain.com",
"substitution_data": {
"replyto": "myReplyToh@yourdomain.com",
"from": "MyFrom@yourdomain.com",
"subject": "my subject",
"body1": "Extra content for the HTML part",
"body2": "Extra content for the text part"
},
"recipients": [
{
"substitution_data": {},
"address": {
"email": "test1@domain.com",
"name": "test1"
}
}
],
"content": {
"template_id": "xxxxxxx",
"use_draft_template": true
}
}
来自邮件客户端 API 的模板
如果您使用的是编程语言,并且有一个用于撰写电子邮件的库,则可以使用它来编程地创建模板或甚至内联发送消息。这里是一个使用 JavaMail 通过 SparkPost 的传输端点执行这些操作的示例。这种方法应该可以很容易地转换为 PHP 或您选择的编程语言。
public class App extends SparkPostBaseApp {
public static void main(String[] args) throws Exception {
Logger.getRootLogger().setLevel(Level.DEBUG);
App app = new App();
app.runApp();
}
private void runApp() throws Exception {
Message message = createMultipartMessage();
String rfc822Content = getMessageAsString(message);
rfc822Content = "To: {{address.email}}\r\nFrom: {{from}}\r\n" + rfc822Content;
String fromAddress = getFromAddress();
String[] recipients = getTestRecipients();
sendEmail(fromAddress, recipients, rfc822Content);
}
private void sendEmail(String from, String[] recipients, String email)
throws SparkPostException, IOException {
Client sparkpostClient = newConfiguredClient();
TransmissionWithRecipientArray transmission = new TransmissionWithRecipientArray();
List<RecipientAttributes> recipientArray = new ArrayList<>();
for (String recipient : recipients) {
RecipientAttributes recipientAttribs = new RecipientAttributes();
recipientAttribs.setAddress(new AddressAttributes(recipient));
recipientArray.add(recipientAttribs);
}
transmission.setRecipientArray(recipientArray);
transmission.setReturnPath(from);
Map<String, String> substitutionData = new HashMap<>();
substitutionData.put("from", from);
substitutionData.put("name", "Your Name Here");
transmission.setSubstitutionData(substitutionData);
TemplateContentAttributes contentAttributes = new TemplateContentAttributes();
contentAttributes.setEmailRFC822(email);
transmission.setContentAttributes(contentAttributes);
RestConnection connection = new RestConnection(sparkpostClient, getEndPoint());
Response response = ResourceTransmissions.create(connection, 0, transmission);
if (response.getResponseCode() == 200) {
System.out.println("✅ Transmission Response: " + response);
} else {
System.err.println("❌ TRANSMISSION ERROR: " + response);
}
}
private Message createMultipartMessage() throws MessagingException {
Properties props = new Properties();
props.put("mail.smtp.host", "none");
Session session = Session.getDefaultInstance(props, null);
Message message = new MimeMessage(session);
message.setSubject("A multipart MIME message demo");
Multipart multiPart = new MimeMultipart("mixed");
MimeMultipart altPart = new MimeMultipart("alternative");
MimeBodyPart textPart = new MimeBodyPart();
textPart.setText("{{name}},\r\nplain text content", "utf-8");
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent("<b>{{name}},<br><br>Our HTML content</b>", "text/html; charset=utf-8");
altPart.addBodyPart(textPart);
altPart.addBodyPart(htmlPart);
MimeBodyPart altBodyPart = new MimeBodyPart();
altBodyPart.setContent(altPart);
multiPart.addBodyPart(altBodyPart);
MimeBodyPart attachmentPart = new MimeBodyPart();
String filename = "java_SparkPost_background.pdf";
DataSource source = new FileDataSource(filename);
attachmentPart.setDataHandler(new DataHandler(source));
attachmentPart.setFileName(filename);
multiPart.addBodyPart(attachmentPart);
message.setContent(multiPart);
return message;
}
private String getMessageAsString(Message msg) throws IOException, MessagingException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
msg.writeTo(out);
return out.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 not found! " + e.getMessage());
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
结论
现在您已经了解了如何使用 SparkPost 发送几乎任何复杂性的电子邮件,您可能想查看“SparkPost 支持在 Apple Watch 上发送电子邮件”,或者查看替换语法以了解如何在您的模板或传输内容中使用“条件表达式”、“if then else”或“数组迭代”。