装饰器在 Nest 控制器中返回 404

新手上路,请多包涵

我正在使用 NestJS 开发后端(顺便说一句,这太棒了)。我有一个类似于下面这个例子的“标准获取实体情况的单个实例”。

 @Controller('user')
export class UserController {
    constructor(private readonly userService: UserService) {}
    ..
    ..
    ..
    @Get(':id')
    async findOneById(@Param() params): Promise<User> {
        return userService.findOneById(params.id);
    }

这非常简单并且有效——但是,如果用户不存在,服务将返回 undefined 并且控制器返回 200 状态代码和空响应。

为了让控制器返回 404,我想出了以下方法:

     @Get(':id')
    async findOneById(@Res() res, @Param() params): Promise<User> {
        const user: User = await this.userService.findOneById(params.id);
        if (user === undefined) {
            res.status(HttpStatus.NOT_FOUND).send();
        }
        else {
            res.status(HttpStatus.OK).json(user).send();
        }
    }
    ..
    ..

这行得通,但代码更多(是的,它可以重构)。

这真的可以使用装饰器来处理这种情况:

     @Get(':id')
    @OnUndefined(404)
    async findOneById(@Param() params): Promise<User> {
        return userService.findOneById(params.id);
    }

任何人都知道这样做的装饰器,或者比上面那个更好的解决方案?

原文由 Rich Duncan 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

最短的方法是

@Get(':id')
async findOneById(@Param() params): Promise<User> {
    const user: User = await this.userService.findOneById(params.id);
    if (user === undefined) {
        throw new BadRequestException('Invalid user');
    }
    return user;
}

装饰器在这里没有意义,因为它会有相同的代码。

注意: BadRequestException 是从 @nestjs/common 导入的;

编辑

一段时间后,我想到了另一个解决方案,它是 DTO 中的装饰器:

 import { registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint } from 'class-validator';
import { createQueryBuilder } from 'typeorm';

@ValidatorConstraint({ async: true })
export class IsValidIdConstraint {

    validate(id: number, args: ValidationArguments) {
        const tableName = args.constraints[0];
        return createQueryBuilder(tableName)
            .where({ id })
            .getOne()
            .then(record => {
                return record ? true : false;
            });
    }
}

export function IsValidId(tableName: string, validationOptions?: ValidationOptions) {
    return (object, propertyName: string) => {
        registerDecorator({
            target: object.constructor,
            propertyName,
            options: validationOptions,
            constraints: [tableName],
            validator: IsValidIdConstraint,
        });
    };
}

然后在你的 DTO 中:

 export class GetUserParams {
    @IsValidId('user', { message: 'Invalid User' })
    id: number;
}

希望它能帮助别人。

原文由 Valera 发布,翻译遵循 CC BY-SA 4.0 许可协议

没有为此内置装饰器,但您可以创建一个 拦截器 来检查返回值并在 NotFoundException 上抛出 undefined

拦截器

@Injectable()
export class NotFoundInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle()
      .pipe(tap(data => {
        if (data === undefined) throw new NotFoundException();
      }));
  }
}

然后您可以使用 Interceptor 将其添加到任一端点:

 @Get(':id')
@UseInterceptors(NotFoundInterceptor)
findUserById(@Param() params): Promise<User> {
    return this.userService.findOneById(params.id);
}

或您的 Controller 的所有端点:

 @Controller('user')
@UseInterceptors(NotFoundInterceptor)
export class UserController {

动态拦截器

您还可以将值传递给拦截器以自定义每个端点的行为。

在构造函数中传递参数:

 @Injectable()
export class NotFoundInterceptor implements NestInterceptor {
  constructor(private errorMessage: string) {}
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  intercept(context: ExecutionContext, stream$: Observable<any>): Observable<any> {
    return stream$
      .pipe(tap(data => {
        if (data === undefined) throw new NotFoundException(this.errorMessage);
                                                            ^^^^^^^^^^^^^^^^^
      }));
  }
}

然后使用 new 创建拦截器:

 @Get(':id')
@UseInterceptors(new NotFoundInterceptor('No user found for given userId'))
findUserById(@Param() params): Promise<User> {
    return this.userService.findOneById(params.id);
}

原文由 Kim Kern 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏