使用SendGrid发送HTML格式附件失败的技术求助及代码问题排查
解决SendGrid发送HTML附件的Bad Request问题
我来帮你搞定这个SendGrid发送HTML附件的问题,咱们一步步拆解问题根源,再给出修复方案:
问题根源分析
你遇到的核心问题有两个:
checked=""属性替换无效:你的代码里content.Replace(@"checked=""", "checked=''");的转义逻辑有问题——C#中@字符串里的""代表单个双引号,所以你实际替换的是checked=",而非目标的checked="",导致替换没生效。- 附件内容未编码:SendGrid要求附件的
Content字段必须是Base64编码的字符串,你直接传入原始HTML文本,这会触发格式验证失败,进而返回Bad Request错误(这也是SMTP能成但SendGrid不行的关键原因,SMTP对附件格式的容错性更高)。
修复后的完整代码
下面是修正后的代码,我标注了关键修改点:
using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using SendGrid; using SendGrid.Helpers.Mail; public static async Task SendMail() // 改成async方法,支持await { try { var apiKey = ""; // 填入你的SendGrid API Key var client = new SendGridClient(apiKey); var from = new EmailAddress("abc@gmail.com"); var tosList = new List<EmailAddress>(); var ToEMailAddress = "abc@gmail.com"; // 处理多个收件人 if (ToEMailAddress.Contains(";")) { foreach (var address in ToEMailAddress.Split(';', StringSplitOptions.RemoveEmptyEntries)) { tosList.Add(new EmailAddress(address.Trim())); } } else { tosList.Add(new EmailAddress(ToEMailAddress)); } var plainTextContent = string.Empty; var htmlContent = "Test Body"; var subject = "Test Subject"; // 修正原代码的拼写错误 var msg = MailHelper.CreateSingleEmailToMultipleRecipients(from, tosList, subject, plainTextContent, htmlContent); var fileName = $"BmaReport_{DateTime.Now:MMMddyyyyHHmmss}.html"; // 简化字符串拼接 string content = @"<!DOCTYPE html><html><head><style type='text/css'>table { font-family: Noto Sans;border-collapse: collapse;width: 100%;} table td, table th { border: 1px solid #ddd;padding: 8px;}table tr:nth-child(even){background-color: #f2f2f2;} table tr:hover { background-color: #ddd;} table th { padding-top: 12px;padding-bottom: 12px;text-align: left;background-color: #04AA6D;color: white;}.togCheck { display: none; }.togContent { max-height: 0; opacity: 0;visibility: hidden;transition: max-height 1s;}.togCheck:checked + .togContent {max-height: 162vh; opacity: 1; visibility: visible;}{font-family: Noto Sans;box-sizing: border-box;}.togButton { display: block; width: 100%;padding: 10px;color: #fff;background: #00778D;cursor: pointer;}.togContent { background: #fefefe00;}.togContent p { padding: 50px 10px;margin: 0;}</style></head><body style='font-family: Noto Sans'><br><br> Record of report submitted via BMA Reporting Portal at 01:54UTC on 19Aug2022<div style='height: 85px;width: 194px;float: right;margin-right: 2%;margin-top: -1%;background-image: url(https://stage-casualtyreporting.azurewebsites.net/Assets/logo-bma.svg);background-repeat: no-repeat;'></div><br><br> Record submitted to BMA<table><tr><td>Reportable Event : Loss of hull integrity</td></tr></table><br><br><h5 for='vesselDetail' class='togButton'>Principal Details</h5><input type='checkbox' checked="" class='togCheck' id='vesselDetail'><div class='togContent'><h5>Vessel and Voyage details</h5><table cellpadding='5' cellspacing='5'><tr><td> Vessel Name : Test loss</td><td> IMO No : 3333333</td><td> Vessel Type : Bulk carrier</td><td> Flag : Bahamas</td></tr><tr><td> No. of Crew on Board : 1</td><td> No. of Passengers : 0</td><td> No. of Supernumeraries : 0</td><td> Voyage From : Test loss</td></tr><tr><td> Voyage to/ Current port (if alongside) : Test loss</td><td> Cargo (if applicable) : </td></tr></table><h5>Reportable Event Details</h5><table cellpadding='5' cellspacing='5'><tr><td> Stage of Passage : Anchored</td><td>Date of Event : 08-01-2022</td></tr><tr><td> Latitude Degree : 33</td><td> Latitude Decimal : 33</td><td> Latitude Direction : N</td></tr><tr><td> Longitude Degree : 33</td><td> Longitude Decimal : 33</td><td> Longitude Direction : E</td><td>What was happening at the time of event (select all that apply) : Ballasting,Bunkering,</td></tr><tr><td>Time of Event (ship/local) : 03:33</td><td>Time Zone : +6</td></tr><tr><table><tr><td>Brief Description : Test loss</td></tr></table></tr></table><h5>Additional consequences of event</h5><table cellpadding='5' cellspacing='5'><tr><td>Injured Person(s) :No</td><td>Pollution :No</td><td>Missing Person : No</td><td>Fatality : No</td><td>Material Damage : No</td></tr></table></div><h5 for='envDetail' class='togButton'>Environment Details</h5><input type='checkbox' checked="" class='togCheck' id='envDetail'><div class='togContent'><table cellpadding='5' cellspacing='5'><tr><td> Weather : Overcast</td><td> Visibility : Poor (Vis 0.5−2nm)</td><td> Light : Daylight</td></tr><tr><td> Wind Speed (Beaufort scale) : 1 (1−3 knots)</td><td> Wind Direction : N</td><td> Sea State : Rippled (0–0.1m)</td></tr></table></div><h5 for='contDet' class='togButton'>Contact Details</h5><input type='checkbox' checked="" class='togCheck' id='contDet'><div class='togContent'><h5>DPA Contact Detail</h5><table cellpadding='5' cellspacing='5'><tr><td>Name : Test loss</td><td>DPA Email : abc.meka@gmail.com</td><td>DPA Phone No : 1234567890</td></tr></table><table><h5>Master's Contact Details</h5></table><table cellpadding='5' cellspacing='5'><tr><td>Name : Test loss</td><td>DPA Email : abc.meka@gmail.com</td><td>DPA Phone No : 1234567890</td></tr></table><h5>Person Completing Form</h5><table cellpadding='5' cellspacing='5'><tr><td>Name : Test loss</td><td>Role : DPA</td><td>Date : 08-02-2022</td><td>Email : abc.meka@gmail.com</td></tr></table></div></body></html>"; // 关键修复1:正确替换checked=""为符合HTML规范的checked属性 content = content.Replace(@"checked=""", "checked"); // 关键修复2:将HTML内容转换为Base64编码(SendGrid要求附件内容必须是Base64) byte[] htmlBytes = Encoding.UTF8.GetBytes(content); string base64Content = Convert.ToBase64String(htmlBytes); var attachment = new SendGrid.Helpers.Mail.Attachment { Filename = fileName, Type = "text/html", Content = base64Content, // 使用Base64编码后的内容 Disposition = "attachment" }; msg.Attachments = new List<SendGrid.Helpers.Mail.Attachment> { attachment }; // 优化:使用await调用异步方法,避免同步调用导致的死锁 var result = await client.SendEmailAsync(msg); // 增加错误排查逻辑:打印响应状态和错误信息 if (result.IsSuccessStatusCode) { Console.WriteLine("邮件发送成功!"); } else { string errorDetails = await result.Body.ReadAsStringAsync(); Console.WriteLine($"发送失败,状态码:{result.StatusCode},错误详情:{errorDetails}"); } } catch (Exception ex) { Console.WriteLine($"发送过程中出现异常:{ex.Message}"); // 处理AggregateException,查看内部异常详情 if (ex is AggregateException aggEx) { foreach (var innerEx in aggEx.InnerExceptions) { Console.WriteLine($"内部异常:{innerEx.Message}"); } } } }
关键修改说明
- 修复
checked属性替换:把checked=""替换为符合HTML5规范的checked,同时修正了替换逻辑,确保能匹配到目标内容 - Base64编码附件内容:SendGrid的Attachment.Content字段要求必须是Base64编码的字符串,这是解决Bad Request的核心步骤
- 异步方法优化:将方法改为
async Task,用await调用SendEmailAsync,避免同步调用异步方法可能引发的死锁问题 - 增强错误排查:添加了响应状态码判断和错误详情获取逻辑,方便你快速定位问题
- 收件人处理优化:添加了
StringSplitOptions.RemoveEmptyEntries和Trim(),避免空收件人地址的问题
额外排查建议
- 检查SendGrid API Key权限:确保你的API Key拥有
Mail Send权限 - 查看SendGrid后台日志:登录SendGrid控制台,进入「Email Activity」查看邮件的详细状态和错误原因
- 验证HTML格式:可以用HTML验证工具检查你的HTML是否有语法错误,SendGrid对HTML的格式要求比SMTP更严格
内容的提问来源于stack exchange,提问作者Developer
相关产品推荐
相关产品推荐

