I have been working to validate a request using the class-validator, and NestJS validation plus trying to validate the header contents.
My basic interfaces are all working, but now I am trying to compare some header field data the same way.
I had this question about the custom decorator to try to handle the headers, but the solution to that question, will return the one header. I want to be able to handle them all, similar to how all the body() data is processed.
I need to be able to create a custom decorator for extracting the header fields, and being able to pass them into the class-validator DTO.
For Instance, I want to validate three header fields, such as:
User-Agent = 'Our Client Apps'
Content-Type = 'application/json'
traceabilityId = uuid
There are more fields, but if I can get this going, then I can extrapolate out the rest. I have a simple controller example:
@Controller(/rest/package)
export class PackageController {
constructor(
private PackageData_:PackageService
)
{ }
...
@Post('inquiry')
@HttpCode(HttpStatus.OK) // Not creating data, but need body, so return 200 OK
async StatusInquiry(
@RequestHeader() HeaderInfo:HeadersDTO, // This should be the Headers validation using the decorator from the question above.
I am trying to validate that the headers of the request contain some specific data, and I am using NestJS. I found this information. While this is what I want to do, and it looks proper, the ClassType reference does not exist, and I am not sure what to use instead.
From the example, the decorator is referring to.
request-header.decorator.ts
export interface iError {
statusCode:number;
messages:string[];
error:string;
}
export const RequestHeader = createParamDecorator(
async (value: any, ctx: ExecutionContext) => {
// extract headers
const headers = ctx.switchToHttp().getRequest().headers;
// Convert headers to DTO object
const dto = plainToClass(value, headers, { excludeExtraneousValues: true });
// Validate
const errors: ValidationError[] = await validate(dto);
if (errors.length > 0) {
let ErrorInfo:IError = {
statusCode: HttpStatus.BAD_REQUEST,
error: 'Bad Request',
message: new Array<string>()
};
errors.map(obj => {
AllErrors = Object.values(obj.constraints);
AllErrors.forEach( (OneError) => {
OneError.forEach( (Key) => {
ErrorInfo.message.push(Key);
});
});
// Your example, but wanted to return closer to how the body looks, for common error parsing
//Get the errors and push to custom array
// let validationErrors = errors.map(obj => Object.values(obj.constraints));
throw new HttpException(`${ErrorInfo}`, HttpStatus.BAD_REQUEST);
}
// return header dto object
return dto;
},
I am having trouble generically mapping the constraints into a string array.
My HeadersDTO.ts:
import { Expose } from 'class-transformer';
import { Equals, IsIn, IsString } from 'class-validator';
export class HeadersDTO {
@IsString()
@Equals('OurApp')
@Expose({ name: 'user-agent' })
public readonly 'user-agent':string;
@IsString()
@IsIn(['PRODUCTION', 'TEST'])
public readonly operationMode:string;
}
Headers being sent via Postman for the request:
Content-Type:application/json
operationMode:PRODUCTION
Accept-Language:en
I have just tested the following code and this is working. I think you are missing appropriate type over here,
async StatusInquiry(
@RequestHeader() HeaderInfo:HeadersDTO,
You should have HeadersDTO passed in as param in RequestHeader Decorator this @RequestHeader(HeadersDTO) HeaderInfo:HeadersDTO,
Then I have created customDecorator.ts like this,
export const RequestHeader = createParamDecorator(
//Removed ClassType<unknown>,, I don't think you need this here
async (value: any, ctx: ExecutionContext) => {
// extract headers
const headers = ctx.switchToHttp().getRequest().headers;
// Convert headers to DTO object
const dto = plainToClass(value, headers, { excludeExtraneousValues: true });
// Validate
const errors: ValidationError[] = await validate(dto);
if (errors.length > 0) {
//Get the errors and push to custom array
let validationErrors = errors.map(obj => Object.values(obj.constraints));
throw new HttpException(`Validation failed with following Errors: ${validationErrors}`, HttpStatus.BAD_REQUEST);
}
// return header dto object
return dto;
},
);
My HeadersDTO.ts
file
export class HeadersDTO
{
@IsDefined()
@Expose({ name: 'custom-header' })
"custom-header": string; // note the param here is in double quotes
}
The reason i found this when I looked compiled TS file, it looks like this,
class HeadersDTO {
}
tslib_1.__decorate([
class_validator_1.IsDefined(),
class_transformer_1.Expose({ name: 'custom-header' }),
tslib_1.__metadata("design:type", String)
], HeadersDTO.prototype, "custom-header", void 0);
exports.HeadersDTO = HeadersDTO;
I get following error when i do not pass the header ,
[
ValidationError {
target: HeadersDTO { 'custom-header': undefined },
value: undefined,
property: 'custom-header',
children: [],
constraints: { isDefined: 'custom-header should not be null or undefined' }
}
]