许多开发团队在使用 SendGrid Ja va SDK 构建 HTML 邮件时,都会遇到一个典型问题:前端用户通过 textarea 提交了一段多行文本,但收到的邮件中所有内容都挤成了一行。原本的换行符(\n)在 HTML 渲染过程中被浏览器当作普通空白符合并,导致段落结构完全丢失。
根本原因在于 HTML 的渲染规范:纯文本中的换行符 \n 默认不会被渲染为可见的换行。要让邮件内容实现真正的段落分隔,有两种常见方式:使用 标签,或借助 CSS 属性 white-space: pre-wrap。但后者由于不会对用户输入进行转义,在处理不可信数据时存在 XSS 安全隐患,通常不推荐采用。
那么,在 SendGrid 的 Ja va 环境里,应该如何正确且安全地将用户输入的换行符转换为邮件中的实际换行呢?
根本原因:HTML 对换行符的“无视”
本质上,浏览器渲染 HTML 时仅将 标签识别为明确的换行指令。其他纯文本中的回车或换行符,都被视为空白符并被合并为一个空格。前端提交的 \n 或 \r\n 自然也不例外。因此,核心问题转化为:服务端在拼接邮件模板时,如何将这些原始换行符“翻译”为浏览器可识别的换行标签。
推荐方案:服务端预处理 + 安全转义
最可靠的方案并非依赖模板引擎,而是在 Ja va 服务端获取用户输入字符串后,首先进行 HTML 转义,随后再替换换行符。处理顺序极其重要:必须严格遵守“先转义,后换行”的原则。
import org.apache.commons.text.StringEscapeUtils;
public static String convertNewlinesToBr(String input) {
if (input == null) return "";
// 1. 先进行 HTML 转义(防止 XSS)
String escaped = StringEscapeUtils.escapeHtml4(input);
// 2. 将换行符替换为
(注意:仅替换 \n,\r\n 已被标准化为 \n)
return escaped.replace("\n", "
");
}然后,在构建 SendGrid 邮件内容时,直接将处理后的字符串嵌入模板即可:
Email from = new Email("sender@example.com");
String subject = "Your Dynamic Email";
Email to = new Email("recipient@example.com");
// 假设 frontendText 来自 HTTP 请求体
String frontendText = request.getParameter("message");
String htmlSafeWithBr = convertNewlinesToBr(frontendText);
Content content = new Content("text/html",
"Message:
" + htmlSafeWithBr + "
");
Mail mail = new Mail(from, subject, to, content);
// ... 发送逻辑(使用 SendGrid API client)关键注意事项
- 顺序不可颠倒:务必先调用
StringEscapeUtils.escapeHtml4()对用户输入进行完整 HTML 转义,然后再执行replace将换行符替换为。若顺序颠倒,转义函数会同时将已插入的标签转义为纯文本,导致换行效果完全失效。 - Ja va 里没有现成的 nl2br:PHP 提供的
nl2br()函数在 Ja va 里并不存在。因此手动实现“先转义后替换”的两步流程,是目前最安全且可控的方案。 - CSS 备选方案(慎用):在某些模板完全可控且数据绝对可信的特殊场景下,可为目标容器设置
style="white-space: pre-wrap;",这样无需字符串替换即可保留原始换行格式。但需要重申:该方案仅适用于完全可信的数据源,常规业务场景下强烈不建议使用。
{{unescaped_user_input}}
总结
在 SendGrid Ja va SDK 中安全展示用户多行文本的核心要点在于“先转义,后换行”。通过 StringEscapeUtils.escapeHtml4() 有效防御 XSS 攻击,再配合 replace("\n", " 实现语义化的换行处理,从而在 HTML 邮件中稳定、安全地还原用户输入的原始段落结构。
")
