开发者问题收集

为什么抛出这个错误会导致空白响应,而其他错误却可以正常工作?

2021-03-23
2539

我正在使用 NestJS 做一个项目,在开发组件的服务和控制器时,我使用 try - catch 方法处理错误,然后抛出它们。但是,我遇到了一个我无法抛出的错误,因为它导致响应主体为空白,而其他错误则正常。例如:

这是我的 upload.service.ts 中的一个(有效)函数:

async saveFile(body, file) {
    try {
      const uploadRepository = getRepository(Upload);
      const result = await uploadRepository.save({
        // assign properties [...]
      });
      return result;
    } catch (error) {
      unlink(file.path);
      console.log(error);  // Output 1 
      throw new BadRequestException(error);  // Response 1
    }
}

因此,当我故意发出错误请求(通过不在请求正文中发送必需值)时,我会在控制台中收到此信息:
(输出 1)

QueryFailedError: null value in column "type" of relation "upload" violates not-null constraint
    at new QueryFailedError (%PROJECT_DIRECTORY%\node_modules\typeorm\error\QueryFailedError.js:11:28)
    at PostgresQueryRunner.<anonymous> (%PROJECT_DIRECTORY%\node_modules\typeorm\driver\postgres\PostgresQueryRunner.js:247:31)
    at step (%PROJECT_DIRECTORY%\node_modules\typeorm\node_modules\tslib\tslib.js:141:27)
    at Object.throw (%PROJECT_DIRECTORY%\node_modules\typeorm\node_modules\tslib\tslib.js:122:57)
    at rejected (%PROJECT_DIRECTORY%\node_modules\typeorm\node_modules\tslib\tslib.js:113:69)
    at processTicksAndRejections (internal/process/task_queues.js:93:5) {
  length: 393,
  severity: 'ERROR',
  code: '23502',
  detail: 'Failing row contains (51, 15, 2021-03-23 10:52:59.763387, 2021-03-23 10:52:59.763387, example.pdf, application/pdf, uploads\\e0f65f7ab43f1c226b37a9463f3a4745, null).',
  [...]
}

并且使用 Postman 在响应正文中返回正确的错误:
(响应 1)

{
    "message": "null value in column \"type\" of relation \"upload\" violates not-null constraint",
    "length": 393,
    "name": "QueryFailedError",
    "severity": "ERROR",
    "code": "23502",
    "detail": "Failing row contains (51, 15, 2021-03-23 10:52:59.763387, 2021-03-23 10:52:59.763387, example.pdf, application/pdf, uploads\\e0f65f7ab43f1c226b37a9463f3a4745, null).",
    [...]
}

现在,这是 upload.service.ts 中的另一个函数:

async readFile(res, fileId) {
    try {
      const uploadRepository = getRepository(Upload);
      const found = await uploadRepository.findOne({ id: fileId });

      res.download(found.path, found.originalName);
    } catch (error) {
      console.log(error);  // Output 2
      throw new BadRequestException(error);  // Response 2
    }
  }

当我故意发出错误请求(通过发送 uploadRepository.findOne 找不到的不存在的 ID)时,我会在控制台中收到此信息:
(输出2)

TypeError: Cannot read property 'path' of undefined
    at UploadsService.readFile (%PROJECT_DIRECTORY%\dist\src\components\uploads\uploads.service.js:52:32)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async %PROJECT_DIRECTORY%\node_modules\@nestjs\core\router\router-execution-context.js:46:28
    at async %PROJECT_DIRECTORY%\node_modules\@nestjs\core\router\router-proxy.js:9:17

但使用 Postman 时,响应主体中有一个空白对象,尽管它正确地具有 400 状态代码:
(响应 2)

{}

我可以看出,前一个错误(如 console.log 输出中所示)具有一个具有更多属性的对象。但是,我不知道为什么我无法正确返回后一个错误。我也尝试过:

throw new NotFoundException({ message: error });
throw new NotFoundException(JSON.stringify(error));
throw new NotFoundException(error.TypeError);

没有运气,因为它们在 message 键中显示一个空对象。

因此, 问题是 :这些错误有何不同,发送后者的正确方法是什么?

2个回答

有趣的问题让我深入研究了 NestJS 源代码。所以,这就是发生的事情。

在抛出传递了 TypeErrorBadRequestException 之后(请注意,这来自 NodeJS,而不是 NestJS), BaseExceptionFilter 会捕获错误并将其传递给 ExpressAdapter (假设您使用的是 express)。在那里, 在以下行 ,它们执行:

return isObject(body) ? response.json(body) : response.send(String(body));

body 变量引用 TypeError 本身并且是一个对象,因此执行 response.json(body) 。 问题是,在 response.json 函数内部,传递给它的对象使用 JSON.stringify 进行 stringifed 。 由于 TypeError 没有可枚举属性,因此您将获得一个空对象字符串 {}。

如果您想知道 为什么 如此,请查看此 SO-post

因此,为了解决这个问题,您可以创建自己的错误对象而不是 TypeError ,并将其传递给 BadRequestException

eol
2021-03-23

在收到 eol 的回答后,我想知道“我怎样才能看到一个物体的结构”?于是我进入了 Object.getOwnPropertyNames() 方法,该方法“ 返回在给定对象中直接找到的所有属性( 包括不可枚举的属性 ,但使用 Symbol 的属性除外)的数组。 ” 在添加 console.log(Object.getOwnPropertyNames(error)); 后,输出为 [ 'stack', 'message' ]

由于 message 属性是一个字符串,因此最终有效的是:

throw new NotFoundException(error.message);
oScarDiAnno
2021-03-23