Как изменить проверку во вложенном объекте, используя тот же класс в NestJS

#typescript #nestjs #class-validator

Вопрос:

У меня есть интерфейсное приложение, которое отправляет в мое серверное приложение запрос POST со следующим телом JSON

 {
    "principal": {
        "name": "John Doe",
        "birth": "1990-01-01T02:00:00.000Z",
        "phone": "(12) 341 234 124",
        "email": "test@test.com"
    },
    "companion": {
        "name": "",
        "birth": null,
        "phone": "",
        "email": ""
    },
    ... // some other data
}

 

И в моем бэкэнд-приложении, которое я использую NestJS, поскольку и то, principal и companion другое имеют одинаковые свойства, я сопоставил со следующим DTO.

 import { IsNotEmpty, IsString } from "class-validator";

export class NewDataDto {
    principal: PersonDto;
    companion: PersonDto;
    // some other data
}

export class PersonDto {
    @IsNotEmpty()
    @IsString()
    name: string;

    birth?: Date;

    @IsNotEmpty()
    @IsString()
    phone: string;

    @IsNotEmpty()
    @IsString()
    email: string;
} 
 

Таким образом, в principal объекте все свойства обязательны, но в companion объекте все свойства являются необязательными. Поскольку все свойства одинаковы в обоих объектах, изменяется только проверка, как я могу проверить это, используя один и тот же класс для обоих объектов?

Комментарии:

1. Вы пробовали github.com/typestack/class-validator#validating-nested-objects ?

2. Да, я пытался. Но у меня есть 2 объекта с разными проверками, которые относятся к одному и тому же классу , поэтому name , например, они должны быть обязательными в principal , но могут быть пустыми в companion .

3. Как насчет создания отдельных классов для целей проверки?

4. Да, я согласен с Бруно, что лучше разделять занятия. А также взгляните на пакеты сопоставленных типов из nest js docs.nestjs.com/openapi/mapped-types

5. Да, это способ решить эту проблему. Но нарушает СУХОЙ принцип так сильно, что отбил у меня охоту следовать этому решению.

Ответ №1:

Итак, я нашел ответ на свою проблему. Действительно, есть способ сделать это. Не используя один и тот же класс, но не причиняя вреда СУХИМ участникам и повторно используя код. Я опубликую это здесь, чтобы это могло принести пользу кому-то, у кого такая же проблема.

 export class PersonDto {
    protected validateRequiredFields = false;
    
    @ValidateIf((o) => o.validateRequiredFields || o.name)
    @IsNotEmpty()
    @IsString()
    name: string;

    birth?: Date;

    @ValidateIf((o) => o.validateRequiredFields || o.phone)
    @IsNotEmpty()
    @IsString()
    phone: string;

    @ValidateIf((o) => o.validateRequiredFields || o.email)
    @IsNotEmpty()
    @IsString()
    email: string;
}

export class PrincipalDto extends PersonDto{
    constructor() {
        super();
        this.validateRequiredFields = true;
    }
}

export class NewDataDto {
    @ValidateNested()
    @Type(() => PrincipalDto)
    @IsDefined()
    principal: PrincipalDto;

    @ValidateNested()
    @Type(() => PersonDto)
    companion: PersonDto;

    // some other data
} 
 

Здесь следует отметить некоторые вещи.

Во-первых, если все классы находятся в одном файле, сначала необходимо объявить классы, которые не имеют зависимости.

Во-вторых, в поле @ValidateIf вам нужно поместить validateRequiredFields предложение с ИЛИ и поле, которое вы проверяете, потому что вы хотите проверить, имеет ли поле какое-либо значение.

В-третьих, при проверке материнского класса, в моем случае NewDataDto , убедитесь, что вы проверяете @Type правильно. И введите a @IsDefined в свойство, которое вы хотите, чтобы необходимые поля были проверены.

И последнее, вы можете переопределить validateRequiredFields внутренний конструктор или внешний, и у вас нет конструктора в подклассе, и это будет работать просто отлично. Но лично мне нравится переопределение в идее конструктора.