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

使用Slack Block Kit发布Lighthouse报告时遇400错误求助

Slack Webhook结合Block Kit发送Lighthouse报告报400 "no_text"错误排查

尝试通过Slack Webhook结合Block Kit将Lighthouse性能报告发布到Slack时,始终收到400状态码,响应信息为"no_text",需排查问题并实现完整报告的格式化发送。

Lighthouse报告JSON格式

{
  "Performance Score": [
    { "Performance": " 59 %", "Color": "ORANGE" },
    { "Accessibility": "  85 %", "Color": "ORANGE" },
    { "BestPractices": "  96 %", "Color": "GREEN" },
    { "SEO": "  100 %", "Color": "GREEN" }
  ],
  "Performance KPI": [
    {
      "First Contentful Paint (FCP)": "1.7 s",
      "Speed Index": "4.7 s",
      "Largest Contentful Paint (LCP)": "8.7 s",
      "Total Blocking Time (TBT)": "70 ms",
      "Cumulative Layout Shift (CLS)": "0.012"
    }
  ]
}

最初构造的Block Kit结构

const reportblocks = [
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "*Performance Score*"
    },
    "fields": [
      {
        "type": "mrkdwn",
        "text": `• Performance: ${payload['Performance Score'][0]['Performance']} (${payload['Performance Score'][0]['Color']})`
      },
      {
        "type": "mrkdwn",
        "text": `• Accessibility: ${payload['Performance Score'][1]['Accessibility']} (${payload['Performance Score'][1]['Color']})`
      },
      {
        "type": "mrkdwn",
        "text": `• Best Practices: ${payload['Performance Score'][2]['BestPractices']} (${payload['Performance Score'][2]['Color']})`
      },
      {
        "type": "mrkdwn",
        "text": `• SEO: ${payload['Performance Score'][3]['SEO']} (${payload['Performance Score'][3]['Color']})`
      }
    ]
  },
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "*Performance KPI*"
    },
    "fields": [
      {
        "type": "mrkdwn",
        "text": `• First Contentful Paint (FCP): ${payload['Performace KPI'][0]['First Contentful Paint (FCP)']}`
      },
      {
        "type": "mrkdwn",
        "text": `• Speed Index: ${payload['Performace KPI'][0]['Speed Index']}`
      },
      {
        "type": "mrkdwn",
        "text": `• Largest Contentful Paint (LCP): ${payload['Performace KPI'][0]['Largest Contentful Paint (LCP)']}`
      },
      {
        "type": "mrkdwn",
        "text": `• Total Blocking Time (TBT): ${payload['Performace KPI'][0]['Total Blocking Time (TBT)']}`
      },
      {
        "type": "mrkdwn",
        "text": `• Cumulative Layout Shift (CLS): ${payload['Performace KPI'][0]['Cumulative Layout Shift (CLS)']}`
      }
    ]
  }
];

收到的错误响应

Slack notification sent successfully. Response: {"status":"fail","statusCode":400,"headers":{"date":"Mon, 08 Apr 2024 16:46:09 GMT","server":"Apache","vary":"Accept-Encoding","strict-transport-security":"max-age=31536000; includeSubDomains; preload","referrer-policy":"no-referrer","x-slack-unique-id":"ZhQfUQd89LozgBIYrwHtqgAAACM","x-slack-backend":"r","access-control-allow-origin":"*","x-frame-options":"SAMEORIGIN","content-type":"text/html","content-length":"7","via":"1.1 slack-prod.tinyspeck.com, envoy-www-iad-lopevkmr, envoy-edge-canary-bom-bccpskqz","x-envoy-attempt-count":"1","x-envoy-upstream-service-time":"217","x-backend":"main_normal main_canary_with_overflow main_control_with_overflow","x-server":"slack-www-hhvm-main-iad-szna","x-slack-shared-secret-outcome":"no-match","x-edge-backend":"envoy-www","x-slack-edge-shared-secret-outcome":"no-match"},"response":"no_text"}

修改后的单一Section结构

const reportblocks = {
  type: "section",
  text: {
    type: "mrkdwn",
    text: `
    *Performance Score:*
    • Performance: ${payload["Performance Score"][0]["Performance"]} (${payload["Performance Score"][0]["Color"]})
    • Accessibility: ${payload["Performance Score"][1]["Accessibility"]} (${payload["Performance Score"][1]["Color"]})
    • Best Practices: ${payload["Performance Score"][2]["BestPractices"]} (${payload["Performance Score"][2]["Color"]})
    • SEO: ${payload["Performance Score"][3]["SEO"]} (${payload["Performance Score"][3]["Color"]})
    
    *Performance KPI:*
    • First Contentful Paint (FCP): ${payload["Performace KPI"][0]["First Contentful Paint (FCP)"]}
    • Speed Index: ${payload["Performace KPI"][0]["Speed Index"]}
    • Largest Contentful Paint (LCP): ${payload["Performace KPI"][0]["Largest Contentful Paint (LCP)"]}
    • Total Blocking Time (TBT): ${payload["Performace KPI"][0]["Total Blocking Time (TBT)"]}
    • Cumulative Layout Shift (CLS): ${payload["Performace KPI"][0]["Cumulative Layout Shift (CLS)"]}
    `,
  },
};

可正常运行的简化代码

const Slack = require("slack-node");
const logger = require("../../reporting/logger");
const apiEndPoints = require("../../config/apiEndPoints");

const webhookurl = `${apiEndPoints.SLACK_SDET_URL}`;

async function sendSlackNotification(payload) {
  try {
    logger.info(`Sending Slack notification to: ${webhookurl}`);
    
    const slack = new Slack();
    slack.setWebhook(webhookurl);
    
    const reportblocks = [
      {
        type: 'section',
        text: { type: 'mrkdwn', text: '*Performance Score:*' },
        fields: payload['Performance Score'].map(score => ({
          type: 'mrkdwn',
          text: `${Object.keys(score)[0]}: ${Object.values(score)[0]}`,
        })),
      }
    ];
    
    const message = {
      blocks: reportblocks ,
    };
    
    slack.webhook(message, function (err, response) {
      if (err) {
        logger.error(`Error occurred while sending Slack notification: ${err}`);
      } else {
        logger.info(`Slack notification sent successfully: ${JSON.stringify(response)}`);
      }
    });
  } catch (error) {
    logger.error("Error occurred:", error);
  }
}

sendSlackNotification();

module.exports = {
  sendSlackNotification,
};

问题排查与解决方案

核心问题分析

  1. 必填字段缺失:Slack Webhook要求消息必须包含text字段作为降级内容(即使使用blocks),仅传blocks会触发"no_text"错误。
  2. 拼写错误:原代码中payload['Performace KPI']存在拼写错误(少一个'r',应为Performance KPI),导致模板字符串渲染出undefined,使内容无效。
  3. 内容渲染异常:拼写错误引发的无效内容会让Slack判定消息无有效文本,进而返回400错误。

修复后的完整代码

const Slack = require("slack-node");
const logger = require("../../reporting/logger");
const apiEndPoints = require("../../config/apiEndPoints");

const webhookurl = `${apiEndPoints.SLACK_SDET_URL}`;

async function sendSlackNotification(payload) {
  try {
    logger.info(`Sending Slack notification to: ${webhookurl}`);
    
    const slack = new Slack();
    slack.setWebhook(webhookurl);
    
    // 修复拼写错误:Performace → Performance
    const performanceKPIs = payload['Performance KPI'][0];
    
    const reportblocks = [
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "*📊 Lighthouse Performance Report*"
        }
      },
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "*Performance Score*"
        },
        "fields": [
          {
            "type": "mrkdwn",
            "text": `• Performance: ${payload['Performance Score'][0]['Performance']} (${payload['Performance Score'][0]['Color']})`
          },
          {
            "type": "mrkdwn",
            "text": `• Accessibility: ${payload['Performance Score'][1]['Accessibility']} (${payload['Performance Score'][1]['Color']})`
          },
          {
            "type": "mrkdwn",
            "text": `• Best Practices: ${payload['Performance Score'][2]['BestPractices']} (${payload['Performance Score'][2]['Color']})`
          },
          {
            "type": "mrkdwn",
            "text": `• SEO: ${payload['Performance Score'][3]['SEO']} (${payload['Performance Score'][3]['Color']})`
          }
        ]
      },
      {
        "type": "divider"
      },
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "*Performance KPI*"
        },
        "fields": [
          {
            "type": "mrkdwn",
            "text": `• FCP: ${performanceKPIs['First Contentful Paint (FCP)']}`
          },
          {
            "type": "mrkdwn",
            "text": `• Speed Index: ${performanceKPIs['Speed Index']}`
          },
          {
            "type": "mrkdwn",
            "text": `• LCP: ${performanceKPIs['Largest Contentful Paint (LCP)']}`
          },
          {
            "type": "mrkdwn",
            "text": `• TBT: ${performanceKPIs['Total Blocking Time (TBT)']}`
          },
          {
            "type": "mrkdwn",
            "text": `• CLS: ${performanceKPIs['Cumulative Layout Shift (CLS)']}`
          }
        ]
      }
    ];
    
    const message = {
      // 添加必填的text降级内容
      text: "Lighthouse性能报告已生成,详情请查看下方内容",
      blocks: reportblocks
    };
    
    slack.webhook(message, function (err, response) {
      if (err) {
        logger.error(`Error occurred while sending Slack notification: ${err}`);
      } else {
        logger.info(`Slack notification sent successfully: ${JSON.stringify(response)}`);
      }
    });
  } catch (error) {
    logger.error("Error occurred:", error);
  }
}

// 调用时需传入正确的Lighthouse报告payload
// sendSlackNotification(yourLighthousePayload);

module.exports = {
  sendSlackNotification,
};

关键修复点

  1. 添加text字段:在消息对象中加入降级文本,满足Slack Webhook的必填要求。
  2. 修正拼写错误:将Performace KPI改为Performance KPI,确保数据能正确读取。
  3. 优化Block结构:添加标题和分隔线提升可读性,简化KPI字段的显示文本。
  4. 变量提取:将KPI对象单独提取,减少重复代码,提升可维护性。

内容的提问来源于stack exchange,提问作者Jyoti Prakash Mallick

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 05:02:03