【问题标题】:How do I organise throwing business-logic exceptions in NestJs services?如何在 NestJs 服务中组织抛出业务逻辑异常?
【发布时间】:2019-12-25 08:54:19
【问题描述】:

我对这个 github 问题讨论有一些疑问:https://github.com/nestjs/nest/issues/310

我想按照shekohex 的建议从我的服务方法中抛出业务域异常。我想知道我应该把它们放在哪里?我相信它们中的许多将在跨域(如EntityNotFoundExceptionActionForbiddenException 等)中非常相似,因此将它们保留在应用程序级别(某种共享模块)是有意义的。另一方面,这使得特定域的独立性降低(例如,如果我需要在未来某个时间将其中的一些提取到另一个应用程序中怎么办?)。更重要的是,一些异常可能是特定于域的,我必须将它们保存在适当的域模块结构中。

假设我确实将其中一些保留在共享模块中,而将其余部分保留在各个域目录中。如何将它们映射到正确的HttpExceptions?如果我制作全局异常过滤器,我也会让我的域控制器更加依赖于应用层。如何映射特定于域的异常?我是否要创建另一个模块级异常过滤器?

创建全局或模块级过滤器是否可行?用UseFilters 装饰每个端点似乎很麻烦。

提前感谢您的意见!

【问题讨论】:

    标签: javascript node.js typescript architecture nestjs


    【解决方案1】:

    这是我最近一直在处理的一个很好的问题。

    对于某些项目,我们希望在应用程序的不同域之间共享业务错误。我们最终得到的是简单地拥有一个共享(或公用)文件夹或模块,以暴露我们在应用程序中遇到的不同域特定错误。

    除此之外,我们还使用了 Nest 的常见异常,这些异常非常适合这项工作!

    最后,我们使用共享域特定的业务错误自定义常见的 Nest 异常。

    极简user.service.ts复现例子:

    import { NotFoundException, Logger } from '@nestjs/common';
    import { UserBusinessErrors } from '../../shared/errors/user/user.business-errors';
    
    // some code ...
    
    // Find the users from the usersIds array
    const dbUsers = await this.userRepository.find({ id: In(usersIds) });
    // If none was found, raise an error
    if (!dbUsers) {
        Logger.error(`Could not find user with IDs: ${usersIds}`, '', 'UserService', true);
        throw new NotFoundException(UserBusinessErrors.NotFound);
    }
    

    与其对应的user.business-errors.ts 文件位于共享或公共模块/文件夹中(例如src/shared/errors/user/user.business-errors.ts):

    export const UserBusinessErrors = {
        // ... other errors
    
        NotFound: {
            apiErrorCode: 'E_0002_0002',
            errorMessage: 'User not found',
            reason: `Provided user ids doesn't exist in DB`
        },
    
        // ... other errors
    }
    

    或者有时如果你想要更通用的东西,你也可以使用共享错误:

    import { InternalServerErrorException, Logger } from '@nestjs/common';
    import { SharedBusinessErrors } from '../../shared/errors/shared.business-errors';
    
    // some code ...
    
    try {
        // ...
    } catch (error) {
        Logger.log(SharedBusinessErrors.DbSaveError, 'UserService');
        throw new InternalServerErrorException(SharedBusinessErrors.DbSaveError, error.stack);
    }
    

    对应的shared.business-errors.ts文件:

    export const SharedBusinessErrors = {
        DbSaveError: {
            apiErrorCode: 'E_0001_0001',
            errorMessage: `Error when trying to save resource into the DB`,
            reason: 'Internal server error'
        },
    
        // ... other errors
    }
    

    最后想对您的不同问题发表评论:

    • 如你所说

    创建全局或模块级过滤器是否可行?用 UseFilters 装饰每个端点似乎很麻烦。

    • 我们也可以利用Nest filters customization 并在控制器或路由级别使用装饰器。但对我来说,您实际上仍然需要指定它与哪些业务错误相关,以便为您的业务错误增加价值并且无论如何您都需要在服务中引发错误。因此,只有一些包含域特定(或业务)错误的共享文件和更通用的错误在 imo 中很好(但可能不是最干净/最优雅的解决方案)。

    • 您可以将业务错误文件放在每个模块的文件夹中,并添加一个 webpack 配置以将所有这些文件收集并连接到一个唯一的文件中。

    最后,如果您需要跨多个项目的特定领域或共享错误,我猜您甚至可以创建一个 npm 包。

    如果我有任何不清楚的地方或/以及它是否能让您更深入地了解,请告诉我!

    【讨论】:

      【解决方案2】:

      好的,让我们专注于将异常保存在共享文件夹中,我们得到了您所说的为 HttpExceptions 创建全局异常过滤器的方法,这不是一个坏方法,您可以在异常本身中格式化输出。其他方法可能是创建一个 BusinessDomainFilter 并仅捕获与域相关的异常:

      @Catch(EntityNotFoundException, ActionForbiddenException)
      @Injectable()
      export class BusinessDomainFilter implements ExceptionFilter {
      ...
      

      只映射那些异常,将其余的留给应用程序过滤器

      【讨论】:

        猜你喜欢
        • 2012-05-20
        • 2018-06-06
        • 1970-01-01
        • 1970-01-01
        • 2021-12-26
        • 1970-01-01
        • 2010-12-24
        • 1970-01-01
        • 2016-11-14
        相关资源
        最近更新 更多