简体   繁体   中英

NestJS/Express: Case-Insensitive Body Fields

I'm struggling to make the fields of my request DTOs case insensitive.

export class ExampleDto {
  dateOfBirth?: string
}

Now I want to accept

  • { "dateofbirth": "19880101" }
  • { "dateOfBirth": "19880101" }
  • { "DATEOFBIRTH": "19880101" }

My first thought was to implement a middleware which just looks at the incoming body and "extends it" with lower & upper case mappings for all incoming fields.

But that doesn't meet my requirements due to camel case, which I definitely want to keep as the default.

Any ideas on how to do this?

You could create a custom Pipe where you try the different options and finally return the Dto instance:

export class CaseInsensitiveExampleDtoPipe implements PipeTransform{
  transform(body: any, metadata: ArgumentMetadata): ExampleDto {
    const dto = new ExampleDto();
    dto.dateOfBirth = body.dateOfBirth || body.dateofbirth || body.DATEOFBIRTH;
    return dto;
  }

In your controller you can then use it as follows:

@UsePipes(new CaseInsensitiveExampleDtoPipe())
async postNewExample(@Body() exampleDto: ExampleDto) {
  // ...
}

Since JavaScript properties start existing after their initialization, you cannot "see" the definition of dateOfBirth?: string and therefor you won't be able to match it against the received JSON.

A possible solution for that is to enforce the creation of the properties of all of your DTO's with a constructor:

export class ExampleDto {
    dateOfBirth: string

    constructor(dateOfBirth: string){
        this.dateOfBirth = dateOfBirth;
    }
}

Then, you'll be able to iterate over the ExampleDto 's properties and match them with a pipe (the received type can be derived from metadata ):

@Injectable()
export class IgnoreCasePipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    const dto = new metadata.metatype;
    const dtoKeys = Object.getOwnPropertyNames(dto);
    Object.keys(value).forEach(key => {
      const realKey = dtoKeys.find(dtoKey => dtoKey.toLocaleLowerCase() === key.toLocaleLowerCase());
      if (realKey) {
        dto[realKey] = value[key];
      }
    });
    return dto;
  }
}

Either inject it globally in main.ts or wherever it's needed - just bear in mind that you'll need to create a constructor for each DTO.

Note: this would work for a single-level class. If you want to support something like people: PersonDto[] in your classes then you'll need to recursively find all of the nested keys and match them - something like this .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM