You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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&minus;2nm)</td><td> Light : Daylight</td></tr><tr><td> Wind Speed (Beaufort scale) : 1 (1&minus;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}");
            }
        }
    }
}

关键修改说明

  1. 修复checked属性替换:把checked=""替换为符合HTML5规范的checked,同时修正了替换逻辑,确保能匹配到目标内容
  2. Base64编码附件内容:SendGrid的Attachment.Content字段要求必须是Base64编码的字符串,这是解决Bad Request的核心步骤
  3. 异步方法优化:将方法改为async Task,用await调用SendEmailAsync,避免同步调用异步方法可能引发的死锁问题
  4. 增强错误排查:添加了响应状态码判断和错误详情获取逻辑,方便你快速定位问题
  5. 收件人处理优化:添加了StringSplitOptions.RemoveEmptyEntries和Trim(),避免空收件人地址的问题

额外排查建议

  • 检查SendGrid API Key权限:确保你的API Key拥有Mail Send权限
  • 查看SendGrid后台日志:登录SendGrid控制台,进入「Email Activity」查看邮件的详细状态和错误原因
  • 验证HTML格式:可以用HTML验证工具检查你的HTML是否有语法错误,SendGrid对HTML的格式要求比SMTP更严格

内容的提问来源于stack exchange,提问作者Developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 18:07:40