使用 NestJS 和类验证器创建标头自定义验证

Ste*_*ott 6 typescript class-validator nestjs

我一直在努力使用类验证器和 NestJS 验证以及尝试验证标头内容来验证请求。
我的基本接口都可以工作,但现在我尝试以相同的方式比较一些标头字段数据。

我有一个关于自定义装饰器的问题来尝试处理标头,但该问题的解决方案将返回一个标头。我希望能够处理所有这些,类似于处理所有 body() 数据的方式。

我需要能够创建一个自定义装饰器来提取标头字段,并能够将它们传递到类验证器 DTO 中。

例如,我想验证三个标头字段,例如:

User-Agent = 'Our Client Apps'
Content-Type = 'application/json'
traceabilityId = uuid
Run Code Online (Sandbox Code Playgroud)

还有更多的领域,但如果我能做到这一点,那么我就可以推断出其余的领域。我有一个简单的控制器示例:

@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.
Run Code Online (Sandbox Code Playgroud)

我正在尝试验证请求的标头是否包含一些特定数据,并且我正在使用 NestJS。我找到了这个信息。虽然这就是我想要做的,而且看起来很正确,但 ClassType 引用不存在,而且我不确定该使用什么。

从例子来看,装饰器指的是。

请求头.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;
},
Run Code Online (Sandbox Code Playgroud)

我在将约束映射到字符串数组时遇到问题。

我的 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;
}
Run Code Online (Sandbox Code Playgroud)

通过 Postman 发送请求的标头:

Content-Type:application/json
operationMode:PRODUCTION
Accept-Language:en
Run Code Online (Sandbox Code Playgroud)

Soh*_*han 6

我刚刚测试了以下代码,这是有效的。我认为你在这里缺少适当的类型,

    async StatusInquiry(
        @RequestHeader() HeaderInfo:HeadersDTO,
Run Code Online (Sandbox Code Playgroud)

您应该将HeadersDTO作为RequestHeader Decorator中的参数传入@RequestHeader(HeadersDTO) HeaderInfo:HeadersDTO,

然后我像这样创建了 customDecorator.ts,

    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;
    },
);
Run Code Online (Sandbox Code Playgroud)

我的HeadersDTO.ts档案

    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' }
      }
    ]
Run Code Online (Sandbox Code Playgroud)