我正在使用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);
}
任何人都知道这是一个装饰者,或者是一个比上面更好的解决方案吗?
答案 0 :(得分:5)
最简单的方法是
@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;
}
希望它有所帮助。
答案 1 :(得分:3)
没有内置装饰器,但是您可以创建一个interceptor来检查返回值并在NotFoundException
上抛出undefined
:
@Injectable()
export class NotFoundInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, stream$: Observable<any>): Observable<any> {
// stream$ is an Observable of the controller's result value
return stream$
.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);
}
答案 2 :(得分:3)
拦截器API也已简化。此外,由于社区报告了此on the Nestjs docs,因此需要进行更改。
更新的代码:
import { Injectable, NestInterceptor, ExecutionContext, NotFoundException, CallHandler } from '@nestjs/common';
import { Observable, pipe } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class NotFoundInterceptor implements NestInterceptor {
constructor(private errorMessage: string) { }
intercept(context: ExecutionContext, stream$: CallHandler): Observable<any> {
return stream$
.handle()
.pipe(tap(data => {
if (data === undefined) { throw new NotFoundException(this.errorMessage); }
}));
}
}
答案 3 :(得分:0)
如果是简单的情况,我通常会以这种懒惰的方式进行,而不会增加多余的绒毛:
import {NotFoundException} from '@nestjs/common'
...
@Get(':id')
async findOneById(@Param() params): Promise<User> {
const user: User = await this.userService.findOneById(params.id)
if (!user) throw new NotFoundException('User Not Found')
return user
}
答案 4 :(得分:0)
您可以只使用以下内容来发送您想要的响应以及标题中的正确状态代码。
在控制器类中的路由处理程序中:
this.whateverService.getYourEntity(
params.id
)
.then(result => {
return res.status(HttpStatus.OK).json(result)
})
.catch(err => {
return res.status(HttpStatus.NOT_FOUND).json(err)
})
为此,您必须拒绝服务方法中的承诺,如下所示:
const entity = await this.otherService
.getEntityById(id)
if (!entity) {
return Promise.reject({
statusCode: 404,
message: 'Entity not found'
})
}
return Promise.resolve(entity)
这里我只是在服务类中使用了另一个服务。您当然可以直接获取您的数据库或执行获取实体所需的任何操作。