Pass @IsInt() validation for application/x-www-form-urlencoded request type
Asked Answered
R

2

5

When I went through Pipes documentation I noticed that I can't make @IsInt() validation for application/x-www-form-urlencoded request correctly, cause all values which I passed I receive as string values.

My request data looks like this enter image description here

My DTO looks like

import { IsString, IsInt } from 'class-validator';

export class CreateCatDto {
    @IsString()
    readonly name: string;

    @IsInt()
    readonly age: number;

    @IsString()
    readonly breed: string;
}

Validation pipe contains next code

import { PipeTransform, Pipe, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';

@Pipe()
export class ValidationPipe implements PipeTransform<any> {
    async transform(value, metadata: ArgumentMetadata) {
        const { metatype } = metadata;
        if (!metatype || !this.toValidate(metatype)) {
            return value;
        }
        const object = plainToClass(metatype, value);
        const errors = await validate(object);
        if (errors.length > 0) {
            throw new BadRequestException('Validation failed');
        }
        return value;
    }

    private toValidate(metatype): boolean {
        const types = [String, Boolean, Number, Array, Object];
        return !types.find((type) => metatype === type);
    }
}

When I debug this pipe I noticed this state enter image description here Where:

  • value - request body value
  • object - transformed via class-transformer value
  • errors - error object

As you can see errors tell to us that age must be an integer number.

How can I pass @IsInt() validation for application/x-www-form-urlencoded request?

Libraries versions:

P.S: I also create a repository where you can run application to test bug. Required branch how-to-pass-int-validation

UPD: after making changes from accepted answer I faced with problem that I put wrong parsed data to storage. Recorded example

Is it possible to get well parsed createCatDto or what I need to do to save it with correct type structure?

Repellent answered 25/2, 2018 at 20:55 Comment(0)
M
12

All values from a application/x-www-form-urlencoded request are always strings.

So, you could do the following:

import { Transform } from 'class-transformer';
import { IsString, IsInt } from 'class-validator';

export class CreateCatDto {
  @IsString()
  readonly name: string;

  @Transform(value => Number.isNan(+value) ? 0 : +value) // this field will be parsed to integer when `plainToClass gets called`
  @IsInt()
  readonly age: number;

  @IsString()
  readonly breed: string;
}
Mcdevitt answered 26/2, 2018 at 1:40 Comment(8)
There's only one problem: Number.isNan() not exist. We need to use isNaN() instead of it.Repellent
There is another one problem: after passing it through validation I got value with string (I put update to my question). Which is the right way of passing correct data to controller?Repellent
According to your validation pipe, you need to return object variable, not the value variable.Mcdevitt
Is this correct behavior for global pipe changes? I wrote that code based on docs and just wanna know will be this convenient to return object instead of valueRepellent
I use object in projects.Mcdevitt
Ok. Thanks for your help!Repellent
Why would you coerce NaN to zero? Seems rather arbitraryMassasauga
Don't know if this solution was for an old version, but I had to do it like this: @Transform(({ value }) => isNan(+value) ? 0 : +value )Gluck
C
5

Adding @Type(() => Number) resolved the issue for me.

import { Type } from 'class-transformer';
import { IsInt, IsNotEmpty } from 'class-validator';

@IsNotEmpty({ message: '' })
@Type(() => Number)
@IsInt({ message: '' })
project: number;
Crocidolite answered 9/6, 2022 at 21:10 Comment(3)
What is the source of Type decorator?Repellent
It comes from class-transformer. I added the imports in the example.Crocidolite
Better than accepted answerDosimeter

© 2022 - 2024 — McMap. All rights reserved.