一、引言

在NestJS开发中,接口鉴权与日志采集是非常重要的功能。然而,传统的实现方式可能会导致代码的重复堆砌,使控制器变得复杂。自定义装饰器可以有效地解决这个问题,让控制器专注于业务编排,提高代码的可维护性和可扩展性。

二、接口鉴权

2.1 传统鉴权方式的问题

在传统的NestJS应用中,接口鉴权通常是在控制器的方法中直接实现。例如,检查用户是否登录、是否具有特定的权限等。这样做的问题是,每个需要鉴权的接口都要重复编写鉴权逻辑,导致代码冗余。

2.2 自定义装饰器实现鉴权

下面是一个使用自定义装饰器实现接口鉴权的示例:

// 定义一个权限枚举
enum Role {
  ADMIN = 'admin',
  USER = 'user',
}

// 自定义装饰器
function Auth(role: Role) {
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    const originalMethod = descriptor.value;

    descriptor.value = async function (...args: any[]) {
      // 这里实现鉴权逻辑,例如检查用户是否具有指定权限
      const userRole = getCurrentUserRole(); // 假设这个函数可以获取当前用户的角色

      if (userRole!== role) {
        throw new Error('没有权限');
      }

      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}

// 使用装饰器
@Controller('protected-resource')
export class ProtectedResourceController {
  @Get()
  @Auth(Role.ADMIN) // 只有admin角色的用户可以访问这个接口
  getResource() {
    return { message: '这是一个受保护的资源' };
  }
}

在这个示例中,我们定义了一个Auth装饰器,它接受一个Role参数。在装饰器内部,我们检查当前用户的角色是否与指定的角色匹配。如果不匹配,就抛出一个错误。这样,每个需要鉴权的接口只需要添加@Auth装饰器,而不需要重复编写鉴权逻辑。

三、日志采集

3.1 传统日志采集方式的问题

传统的日志采集方式通常是在控制器的方法中手动记录日志。例如,记录请求的参数、响应的结果等。这样做的问题是,每个接口都要手动添加日志记录代码,导致代码冗余。

3.2 自定义装饰器实现日志采集

下面是一个使用自定义装饰器实现日志采集的示例:

// 自定义装饰器
function Log() {
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
    const originalMethod = descriptor.value;

    descriptor.value = async function (...args: any[]) {
      const start = Date.now();
      const result = await originalMethod.apply(this, args);
      const end = Date.now();

      // 这里实现日志记录逻辑,例如记录请求的参数、响应的结果、执行时间等
      console.log({
        method: propertyKey,
        args,
        result,
        time: `${end - start}ms`,
      });

      return result;
    };

    return descriptor;
  };
}

// 使用装饰器
@Controller('logging-resource')
export class LoggingResourceController {
  @Get()
  @Log() // 自动记录这个接口的日志
  getResource() {
    return { message: '这是一个需要记录日志的资源' };
  }
}

在这个示例中,我们定义了一个Log装饰器。在装饰器内部,我们记录了接口的执行时间、请求参数和响应结果。这样,每个需要记录日志的接口只需要添加@Log装饰器,而不需要手动编写日志记录代码。

四、应用场景

自定义装饰器在NestJS中的应用场景非常广泛。除了接口鉴权和日志采集外,还可以用于以下方面:

  1. 输入验证:验证输入参数的合法性。
  2. 错误处理:统一处理接口的错误。
  3. 性能监控:监控接口的性能。

五、技术优缺点

5.1 优点

  1. 代码复用:自定义装饰器可以被多个接口复用,减少代码冗余。
  2. 关注点分离:将横切关注点(如鉴权、日志采集)从业务逻辑中分离出来,使控制器更加纯粹。
  3. 易于维护和扩展:如果需要修改鉴权或日志采集的逻辑,只需要在装饰器中修改,而不需要在每个接口中修改。

5.2 缺点

  1. 学习成本:对于初学者来说,理解和使用自定义装饰器可能需要一定的学习成本。
  2. 调试困难:由于装饰器的实现比较复杂,调试可能会比较困难。

六、注意事项

  1. 装饰器的顺序:如果一个接口有多个装饰器,装饰器的顺序可能会影响其执行效果。
  2. 依赖注入:在装饰器中使用依赖注入时,需要注意注入的顺序和作用域。

七、文章总结

自定义装饰器在NestJS中是一种非常强大的技术,可以有效地简化接口鉴权与日志采集等横切关注点的实现。通过使用自定义装饰器,我们可以让控制器回归纯粹的业务编排,提高代码的可维护性和可扩展性。同时,我们也需要注意装饰器的使用场景、优缺点和注意事项,以确保代码的质量和性能。