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

Node.js调用SendGrid/Mail API发送邮件时出现Bad Request错误——原始请求缺少Header信息

排查SendGrid Bad Request错误的解决方法

先帮你梳理下几个核心问题,以及对应的修复步骤:

1. 先拿到SendGrid的具体错误信息(最关键)

你的错误日志只打印了error.message,但SendGrid的400错误会在error.response.body.errors里给出精准原因(比如发件人未验证、API key权限不足、邮件格式错误等)。先把catch里的日志改成:

.catch((error) => {
  console.error('SendGrid Error Details:', error.response.body.errors);
  console.log('RECEIVED ERROR')
})

这一步能直接帮你定位到底是哪出问题,比如常见的The from address does not match a verified Sender Identity(发件人未验证),或者Permission denied, wrong API key(API key权限不对)。

2. 修复Promise的错误处理逻辑

你的sendMail函数里的Promise完全没做resolve和reject,导致外部的.then()和.catch()根本无法正确捕获结果。而且没必要自己封装Promise,因为sgMail.send()本身就返回Promise,直接用就行:

async function sendMail(mailData) {
  try {
    const msg = {
      to: mailData.to,
      from: mailData.from,
      subject: mailData.subject,
      text: mailData.text,
      html: `<div>${mailData.message}</div>` // 修复转义问题
    }
    console.log("API KEY: " + SENDGRID_API_KEY);
    console.log(JSON.stringify(msg));
    await sgMail.send(msg); // 直接await原生Promise
    console.log('Email sent');
    return 'success';
  } catch (error) {
    console.error('SendGrid Error:', error.response?.body?.errors || error.message);
    throw error; // 把错误抛出去,让外部的catch捕获
  }
}

3. 优化SendGrid的初始化逻辑

sgMail.setApiKey()只需要初始化一次,不用每次调用sendMail都设置。把它移到文件顶部,全局初始化:

const sgMail = require('@sendgrid/mail')
const SENDGRID_API_KEY = process.env.SENDGRID_API_KEY;
sgMail.setApiKey(SENDGRID_API_KEY); // 放在这里,只执行一次

4. 修复HTML内容的转义问题

你原来的html: '&lt;div&gt;'+req.body.message+'&lt;/div&gt;'会把<和>转成实体字符,导致SendGrid发送的邮件里显示的是&lt;div&gt;你的消息&lt;/div&gt;而不是真正的HTML。改成模板字符串直接写标签:

html: `<div>${req.body.message}</div>`

如果req.body.message里有用户输入的HTML,建议用sanitize-html这类库过滤避免XSS,纯文本的话直接用就行。

5. 排查SendGrid平台的配置问题

如果上面的代码改完还是报错,结合第一步拿到的错误信息,检查:

  • 发件人邮箱是否验证:在SendGrid后台的Sender Identity里,必须验证你用的dhook@fullonconsulting.com这个邮箱,否则会被拒绝。
  • API Key的权限:创建API Key的时候,要勾选Mail Send权限,不能只给只读权限。
  • Vercel环境变量是否正确:确认Vercel的环境变量SENDGRID_API_KEY已经正确设置,且部署时生效(可以打印前几位验证:console.log('API Key prefix:', SENDGRID_API_KEY?.slice(0, 5)))。

最后,把主函数改成async函数,await调用sendMail:

export default async function (req, res) { // 加async
  const mailData = {
    from: 'dhook@fullonconsulting.com',
    to: req.body.email,
    subject: 'Message to Full On Consulting',
    text: req.body.message,
    html: `<div>${req.body.message}</div>`
  }
  // 可以删掉原来的Mail.create相关代码,因为你没用到这个生成的body
  try {
    await sendMail(mailData);
    console.log('Email sent...');
    res.status(200).json({ status: 'SUCCESS' })
  } catch (error) {
    console.log('Error ... ', error);
    res.status(400).json({ status: 'FAILED', error: error.response?.body?.errors || error.message });
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 21:19:08