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

NestJS集成Sentry时控制器无法接收数据的问题排查

问题:NestJS接收Sentry Envelope请求时数据为空

我正在开发一个用Sentry做错误追踪的项目,错误数据要发送到本地搭建的简化版Sentry服务器,核心需求是正确处理Sentry请求并获取错误数据用于分析。

在NestJS里写了个控制器处理/api/:projectId/envelope请求,触发错误(比如调用myUndefinedFunction(1))后,Sentry本该向服务器发数据,但控制器里的envelopeData是空的。DSN里的projectId是4,请求能到服务器,但Sentry的请求头没传过来,envelopeData也没有错误信息。

Sentry初始化代码

const Sentry = require('@sentry/node');

Sentry.init({
    dsn: 'http://fde473b41d8547c092d69d8eceb2b9bd@127.0.0.1:3000/4',
});

myUndefinedFunction(1);

NestJS控制器代码

import {
  Controller,
  Post,
  Param,
  Body,
  HttpException,
  HttpStatus,
} from '@nestjs/common';

@Controller('api/:projectId')
export class EnvelopeController {
  @Post('/envelope')
  async handleEnvelope(
    @Param('projectId') projectId: string,
    @Body() envelopeData: any,
  ) {
    try {
      console.log('Received request for projectId:', projectId);
      console.log('Envelope Data:', JSON.stringify(envelopeData, null, 2));

      return { status: 'success', message: 'Envelope processed' };
    } catch (error) {
      console.error('Error during processing:', error);
      throw new HttpException('BAD REQUEST', HttpStatus.BAD_REQUEST);
    }
  }
}

日志中间件代码

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request Headers:', req.headers);
    console.log('Request Body:', req.body);
    console.log('Request Path:', req.path);
    
    if (req.body instanceof Buffer) {
      console.log('Request Body (as string):', req.body.toString('utf-8'));
    }

    next();
  }
}

当前控制台输出

Request Headers: {
  host: '127.0.0.1:3000',
  connection: 'keep-alive',
  'transfer-encoding': 'chunked'
}
Request Body: {}
Request Path: /
Get endpoint for projectId: 4
Envelope Data: {}

疑问

  1. 为什么请求能通过中间件,日志也显示了请求头,但Sentry的请求头没送达,envelopeData为空?
  2. 怎么调试并修复这个问题,让控制器能接收到错误数据?

已采取措施

  • 确认Sentry已经用正确的DSN完成初始化
  • 添加了日志中间件追踪请求头和请求体

预期控制台输出

{
  exception: { values: [ [Object] ] },
  event_id: 'd5816d366c61412eb92e298638cb308b',
  platform: 'node',
  contexts: {
    trace: {
      trace_id: '89b29266023447e2b9b781b51e226b54',
      span_id: 'bbfc1f0e90ac8c7e'
    },
    runtime: { name: 'node', version: 'v20.12.1' },
    app: {
      app_start_time: '2024-10-09T01:38:45.264Z',
      app_memory: 76492800
    },
    os: {
      kernel_version: '6.8.0-45-generic',
      name: 'Ubuntu Linux',
      version: '22.04'
    },
    device: {
      boot_time: '2024-10-08T22:14:04.737Z',
      arch: 'x64',
      memory_size: 16478691328,
      free_memory: 6089416704,
      processor_count: 12,
      cpu_description: '11th Gen Intel(R) Core(TM) i5-11400H @ 2.70GHz',
      processor_frequency: 1622
    },
    // 其他ErrorEvent字段
  }
}

解决方案

问题原因分析

  1. 请求体解析不兼容:Sentry发送的Envelope请求是分块编码的二进制格式,而NestJS默认的body解析器只处理application/json和x-www-form-urlencoded,无法解析这类请求,导致req.body为空。
  2. 路由匹配异常:日志显示请求路径为/,但控制器监听的是/api/:projectId/envelope,说明Sentry实际发送的请求路径与预期不符,存在路由匹配错误。

修复步骤

1. 手动解析原始请求体

安装raw-body依赖后,修改控制器直接读取请求流并解析:

npm install raw-body

修改控制器代码:

import {
  Controller,
  Post,
  Param,
  Req,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import * as rawBody from 'raw-body';

@Controller('api/:projectId')
export class EnvelopeController {
  @Post('/envelope')
  async handleEnvelope(
    @Param('projectId') projectId: string,
    @Req() req: any,
  ) {
    try {
      console.log('Received request for projectId:', projectId);
      // 读取原始二进制请求体
      const envelopeBuffer = await rawBody(req);
      const envelopeStr = envelopeBuffer.toString('utf-8');
      console.log('Raw Envelope Data:', envelopeStr);

      // Sentry Envelope是多行JSON格式,按行解析
      const lines = envelopeStr.split('\n').filter(line => line.trim());
      const envelopeHeader = JSON.parse(lines[0]);
      const eventPayload = lines.length > 1 ? JSON.parse(lines[1]) : null;
      
      console.log('Parsed Header:', envelopeHeader);
      console.log('Parsed Event:', eventPayload);

      return { status: 'success', message: 'Envelope processed' };
    } catch (error) {
      console.error('Processing Error:', error);
      throw new HttpException('BAD REQUEST', HttpStatus.BAD_REQUEST);
    }
  }
}

2. 配置NestJS跳过默认解析

在主模块中针对/envelope路由禁用默认body解析,避免干扰原始请求:

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { EnvelopeController } from './envelope.controller';
import { LoggerMiddleware } from './logger.middleware';
import { json, urlencoded } from 'express';

@Module({
  controllers: [EnvelopeController],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    // 先应用日志中间件
    consumer.apply(LoggerMiddleware).forRoutes('api/:projectId/envelope');
    
    // 全局路由配置:非envelope路由用默认解析,envelope路由跳过
    consumer
      .apply((req, res, next) => {
        if (req.path.includes('/envelope')) {
          next();
        } else {
          json()(req, res, next);
          urlencoded({ extended: true })(req, res, next);
        }
      })
      .forRoutes('*');
  }
}

3. 验证Sentry请求路径与头信息

开启Sentry调试模式,确认请求的URL和头信息是否正确:

const Sentry = require('@sentry/node');

Sentry.init({
    dsn: 'http://fde473b41d8547c092d69d8eceb2b9bd@127.0.0.1:3000/4',
    debug: true, // 打印请求详情
});

myUndefinedFunction(1);

如果头信息缺失,检查CORS配置是否允许所有头:

import { Module } from '@nestjs/common';
import { EnvelopeController } from './envelope.controller';
import { CorsOptions, CorsMiddleware } from '@nestjs/common';

@Module({
  controllers: [EnvelopeController],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    const corsOptions: CorsOptions = {
      origin: '*',
      allowedHeaders: '*',
      exposedHeaders: '*',
    };
    consumer.apply(CorsMiddleware(corsOptions)).forRoutes('*');
  }
}

调试技巧

  • 用curl手动模拟请求验证控制器:
curl -X POST http://127.0.0.1:3000/api/4/envelope \
  -H "X-Sentry-Auth: Sentry sentry_version=7, sentry_key=fde473b41d8547c092d69d8eceb2b9bd" \
  -d '{"event_id": "test-id"}'
  • 查看Sentry客户端调试日志,确认请求是否成功发送,以及请求的URL、头信息是否符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 10:06:01