Я пытаюсь изучить NestJs, создав CRUD API. Я создал свой контроллер, модуль, сервис и т.д.
И создал конечную точку get users/id. Все работало нормально, и я решил добавить немного безопасности. Я хочу проверить, не является ли идентификатор нулевым и является строкой. Если нет, я хочу создать исключение (неверный запрос) + console.info сообщение. Я также хочу проверить, существует ли пользователь, когда я ищу пользователя с хорошим if. если нет, сгенерируйте не найденное исключение.
Вот мой сервис:
async findOne(id: string): Promise<IUser | null> {
if (id === null || typeof id !== 'string') {
throw new BadRequestException('Id must be a string');
}
const user = await this.userModel.findById(id).exec();
if (user === null) {
throw new NotFoundException('No user found for this id');
}
return user;
}
и контроллер:
@Get(':id')
async find(@Param('id') id: string) {
try {
return await this.userService.findOne(id);
} catch (error) {
if (error instanceof BadRequestException) {
throw new HttpException(
{
status: HttpStatus.FORBIDDEN,
error: 'This is a custom message',
},
HttpStatus.FORBIDDEN,
{
cause: error,
},
);
} else if (error instanceof NotFoundException) {
throw new HttpException(
{
status: HttpStatus.NOT_FOUND,
error: 'This is a custom not found message',
},
HttpStatus.NOT_FOUND,
{
cause: error,
},
);
}
}
}
Проблема в том, что когда я пытаюсь получить запрос с помощью .../users/1111, я получаю ответ 200. И когда я пытаюсь с хорошим идентификатором (строкой), но без связанного пользователя, я также получаю ответ 200.
Я не понимаю, почему .. Не могли бы вы мне помочь? Я также хочу зарегистрировать сообщение.
А у вас есть советы? Правильный ли способ (стандартный + элегантный)?
Спасибо ребята ;)
Обновлено: Вот мой контроллер:
@Get(':id')
async find(@Param('id') { id }: IdDto) {
try {
return await this.userService.findOne(id);
} catch (error) {
if (error instanceof NotFoundException) {
return response.status(HttpStatus.NOT_FOUND);
} else {
return response.status(HttpStatus.BAD_REQUEST);
}
}
}
мой сервис:
async findOne(id: string): Promise<IUser | null> {
const user = await this.userModel.findById(id).exec();
if (user === null) {
throw new NotFoundException('No user found for this id');
}
return user;
}
мой дто:
import { IsString, IsNotEmpty, IsMongoId } from 'class-validator';
import { Transform, Type } from 'class-transformer';
import { ObjectId } from 'mongodb';
export class IdDto {
@IsMongoId()
id: string;
}
И когда я ищу .../users/aaaa или /users/63ecf079c305ac977da87bcb (действительный идентификатор mongo) или ...users/1111, я получаю: { "Код статуса": 400, "сообщение": [ «id должен быть идентификатором mongodb» ], "ошибка": "Неверный запрос" }
и я не знаю почему...
Я действительно хочу проверить, действителен ли идентификатор + проверить, существует ли пользователь (если идентификатор действителен). Что я должен делать ?
Спасибо :)
В вашем коде вы проверяете, что идентификатор имеет строку типа, а не нуль. Технически любой параметр является строкой, поэтому даже 1111
становится "1111"
. Вы можете убедиться в этом, зарегистрировав это так console.info({ id })
(ожидаемый результат: { id: "1111" }
).
Для проверки я бы посоветовал следовать документации по каналам проверки: Документация NestJS.
TLDR;
Следующий код добавит глобальный канал для проверки полезной нагрузки.
app.module.ts
(скопировано из NestJS | Pipes)
import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_PIPE,
useClass: ValidationPipe,
},
],
})
export class AppModule {}
Чтобы это работало, вам нужно установить class-validator
и class-transformer
, поэтому запустите:
npm i --save class-validator class-transformer
Затем объявите класс, который будет служить планом DTO (объект передачи данных), например:
import { IsString, IsNotEmpty } from 'class-validator';
export class IdDto {
@IsNotEmpty()
@IsString()
id: string;
}
Затем в вашем контроллере используйте IdDto
:
@Get(':id')
async find(@Param() { id }: IdDto) {
...
Этого уже должно быть достаточно для базовой проверки. Более того, это преобразует полезную нагрузку в формат, который вы ожидаете (или завершится ошибкой и выдаст ошибку проверки). Это делается с помощью plainToClass
метода, представленного class-transformer
. Так что никаких сюрпризов с приведением типов в JavaScript вроде "1" + 1 = "11"
не будет.
Если вам нужно отформатировать ваши исключения (или обогатить их дополнительными данными), вы можете использовать фильтры исключений. Об этом есть хорошая документация в официальной документации.
Надеюсь, это поможет!
Удалите блок try ... catch
в вашем контроллере.
Вот так:
@Get(':id')
async find(@Param('id') id: string) {
return await this.userService.findOne(id);
}
В вашем текущем коде есть аварийный случай. Вы проверяете наличие ошибки instanceof NotFoundException
или instanceof BadRequestException
. Поэтому, если это будет экземпляр HttpException
, для этого нет причин, поэтому ошибка перехватывается, и контроллер отправляет пустой ответ 200
.
Если запрос к базе данных возвращает null, вам нужно бросить NotFoundException
. У вас уже есть необходимая логика в методе user.service
in findOne
. Разве это не желаемое поведение? Какую проблему ты пытаешься решить?
Эй :) Во-первых, мне нужно проверить, действителен ли идентификатор (идентификатор mongo), когда мы используем конечную точку API users/... , идентификатор должен быть действительным. Кроме того, я также хочу проверить, возвращает ли запрос базы данных значение null, а затем выдать исключение NotFoundException. Но как на самом деле поймать это в моем контроллере?
Для проверки идентификатора вы можете использовать декоратор @IsMongoId()
. Но зачем его ловить в контроллере? Разве не логично бросать 404
напрямую? Если вы выдаете ошибку в любом месте кода во время запроса, он будет правильно отправлен клиенту. Пожалуйста, взгляните на Страницу документации NestJS. TLDR; вам не нужно беспокоиться об ошибке, просто выбросьте и забудьте. Если вам нужно отформатировать ошибку, вы можете использовать ExceptionFilters
Посмотрите на мой другой вопрос для IsMongoId() stackoverflow.com/questions/75509265/… пожалуйста :) Потому что я хочу создавать разные исключения и добавлять собственное сообщение :) Но, например, я тестировал с хорошим идентификатором, но это не так. связан с пользователем, и у меня есть ошибка 0, просто статус 200..
Удалите блок try catch из контроллера. просто return await this.userService.findOne(id)
. Я думаю, вы получаете пустой ответ со статусом 200, верно? Если это так, это означает, что операторы if
не совпадают. Вы можете проверить это, добавив условие else { throw new Error('Uncaught error') }
.
Я не понимаю .. что мне делать с пользовательским сервисом и пользовательским контроллером + id dto?
Хорошо, я изменил свой контроллер, как вы сказали: @Get(':id') async find(@Param('id') { id }: IdDto) { return await this.userService.findOne(id); } и мой сервис: async findOne(id: string): Promise<IUser | null> { const user = await this.userModel.findById(id).exec(); if (user === null) { throw new NotFoundException('Пользователь для этого идентификатора не найден'); } вернуть пользователя; } но когда я пытаюсь использовать /63ecf079c305ac977da87bcb, я получаю "statusCode": 400, "message": ["id должен быть строкой"],
Судя по обновленному коду, я понятия не имею, откуда берется id must be a string
. @IsMongoId
выдает такую ошибку: id must be a mongodb id
значит это не то. Можете ли вы убедиться, что вы сохранили код и запускается код с последними изменениями? Самый простой способ сделать это — остановить сервер, удалить папку ./dist
и запустить сервер.
Теперь он выдает «это должен быть идентификатор mongodb», но не понимаю, потому что на самом деле это действительный идентификатор mongo.
Извините, ошибся в ответе. Должно быть @Param()
без id
(код controller
). Извини за это. Пожалуйста, попробуйте, должно быть решено сейчас
Но когда я это делаю, когда я пытаюсь использовать ..../users/aaa, я получаю сообщение об ошибке. И как я могу проверить, существует ли пользователь?