NestJS 控制器中的路由超载?

问题描述 投票:0回答:2

有没有一种漂亮的方法可以在 NestJS 应用程序中创建重载路由?我有一些想法,但也许我正在发明一个轮子。但我找不到任何现成的方法......

我说的是这样的 (让我们以 https://github.com/nestjs/nest/blob/master/sample/01-cats-app/src/cats/cats.controller.ts 作为起点):

@Get()
async findAll(): Promise<Cat[]> {
  return this.catsService.findAll();
}

@Get()
@Roles('admin')
async findAllAdmin(): Promise<Cat[]> {
  return this.catsService.findAllAdmin();
}

换句话说,我希望有两条具有相同 URL 的路由,但通过一些其他值进行区分(例如此处的角色)。

我的想法是创建我自己的装饰器,而不是Get,它将填充一些权重映射,分配给每个重载方法唯一的路径。然后,添加中间件,该中间件将从请求中获取参数,将它们与地图进行比较,并进行内部重定向(使用

next('route')
req.app.handle(req, res)
)到适当的新路径。

但是在这种方法中,如果他们应该在其中一种方法上使用 AuthGuard 进行身份验证,我无法从请求中获取用户......

typescript nestjs
2个回答
0
投票

看起来不太好看,但我们来试试吧。

  1. 创建自定义装饰器
import { createParamDecorator, ExecutionContext } from "@nestjs/common";

export const IsAdmin = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    // Here add the logic to know if is an admin from the request
    return request.role === "admin";
  }
);
  1. 更新您的控制器
@Get()
async findAll(@IsAdmin() isAdmin: boolean): Promise<Cat[]> {
  if (isAdmin) {
    return this.catsService.findAllAdmin();
  }

  return this.catsService.findAll();
}

0
投票

Nest 有两种方法,一种是@David-Morales 已经提出的,但应该有一些改变。

  1. @IsAdmin 装饰器 -> 应用于限制仅管理员对端点的访问

示例:


------file------: is-admin.decorator.ts
import { applyDecorators, UseGuards }             from '@nestjs/common';
import { ApiBearerAuth, ApiUnauthorizedResponse } from '@nestjs/swagger';
import { JwtAuthGuard }                           from '.jwt-auth.guard'; // this is class extending default AuthGuard('jwt') from '@nestjs/passport', but you could use your own there (it must validate user and provide `request.user` variable with user data/role anything you wish to store about user in request).
import { AdminGuard }                             from './admin.guard';

export function IsAdmin() {
  return applyDecorators(
    UseGuards(JwtAuthGuard, AdminGuard),
    ApiBearerAuth(),
    ApiUnauthorizedResponse({ description: 'Unauthorized' }),
  );
}

-----file------: admin.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable }                                from 'rxjs';

@Injectable()
export class AdminGuard implements CanActivate {
  
  canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
    return context.switchToHttp().getRequest().user?.accountType === 'admin';
  }
}
------file------: example controller fully accessible only by admins
/* dont forget proper imports here */
@IsAdmin()
@Controller('/admin/account') // exmaple controller path
export class AccountController {
// any endpoint here can be only accessed by logged in admin
}
------file------: example controller with some routes restricted to admin
/* dont forget proper imports here */
@Controller('/account')
export class AccountController {
  @IsAdmin()
  @Get('/admin')
  async adminRoute() {} // this will be callable only when requesting user is admin

  @Get('/any-user')
  async anyUserRouter() {} // this can be called by anyone
}
  1. @Auth/Invoker/UserContext(任何你想要的名称)装饰器 -> 应该为控制器提供有关用户的信息(角色、上下文、你想要的)。
------file------: invoker.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';


export const Invoker = createParamDecorator(
  (data: unknown, ctx: ExecutionContext): UserAuthorization | undefined => {
    const request = ctx.switchToHttp().getRequest();
    
    if (request.user) {
      return { /*data*/ };
       // here you return class, object - anything that contains use context you want to use. example: return new InvokerClass(request.user); where `InvokerClass`, is class that can tell you user account type, maybe its id etc etc.
     
    }

 return undefined;
  },
);
------file-----: controller that uses both approaches
/* dont forget proper imports here */
@Controller('/account')
export class AccountController {
  @IsAdmin()
  @Get('/admin')
  async adminRoute(@Invoker() invoker: InvokerClass) {} // this will be callable only when requesting user is admin, but you know details of that person in `invoker` variable

  @Get('/any-user')
  async anyUserRouter(@Invoker() invoker?: InvokerClass) {} // this can be called by anyone but you can learn who executed it with `invoker` variable (also get details of that person)
}
© www.soinside.com 2019 - 2024. All rights reserved.