【问题标题】:Create a Header Custom Validation with NestJS and class-validator使用 NestJS 和 class-validator 创建标题自定义验证
【发布时间】:2021-04-07 04:50:19
【问题描述】:

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

我有这个question about the custom decorator 来尝试处理标题,但该问题的解决方案将返回一个标题。我希望能够处理所有这些,类似于处理所有 body() 数据的方式。

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

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

User-Agent = 'Our Client Apps'
Content-Type = 'application/json'
traceabilityId = uuid

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

@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.

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

从例子中,装饰器指的是。

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;

},

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

我的标头DTO.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;
}

通过 Postman 发送请求的标头:

Content-Type:application/json
operationMode:PRODUCTION
Accept-Language:en

【问题讨论】:

  • 我相信你点击了这个链接 - github.com/nestjs/nest/issues/4798。如果是,请交叉检查是否缺少任何东西
  • 是的,除了找不到 ClassType 的问题仍然存在,它不会编译。 import { ClassType } from 'class-transformer/ClassTransformer';
  • 错误是Cannot find module 'class-transformer/ClassTransformer' or its corresponding type declarations.ts(2307)
  • 我已经在我发布的代码下面进行了测试,它对我有用
  • 抱歉,没有看到您将 ClassType 更改为简单的 any。

标签: nestjs class-validator


【解决方案1】:

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

async StatusInquiry(
    @RequestHeader() HeaderInfo:HeadersDTO,

您应该将 HeadersDTO 作为参数传入 RequestHeader 装饰器 this @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;
},

);

我的 HeadersDTO.ts 文件

export class HeadersDTO 
{
  @IsDefined()
  @Expose({ name: 'custom-header' })
  "custom-header": string; // note the param here is in double quotes
}

我在查看编译后的 TS 文件时发现了这个原因,它看起来像这样,

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;

当我没有通过标头时出现以下错误,

[
  ValidationError {
    target: HeadersDTO { 'custom-header': undefined },
    value: undefined,
    property: 'custom-header',
    children: [],
    constraints: { isDefined: 'custom-header should not be null or undefined' }
  }
]

【讨论】:

  • 这确实给了我一个服务器错误,现在是 500,而不是正常的 400 Bad Request。有没有办法让默认处理生效?
  • 您可以添加自定义验证并在await validateOrReject(dto); 函数周围抛出异常。我做了同样的事情,还要注意我现在使用了validate(obj) 方法。更新答案
  • 这确实解决了问题,我可以重新格式化这些。希望得到内部要求的一致性,但可以处理这个。
  • 我没有得到传入的标头值。因此,在运行验证时,它并没有真正进行验证,因为引号中的标题字段没有被填充,因此它们的值是未定义的。
  • 我能够看到我传递的标头值...我只是监视它们并且可以看到它。您是说您无法获取标头值吗?您在哪里尝试访问这些值?
【解决方案2】:

正如issue you referenced 中提到的,您需要创建一个DTO 类并将其传递给RequestHeader 装饰器。

例如


export class MyHeaderDTO {
    @IsString()
    @IsDefined()
    @Expose({ name: 'myheader1' })        // required as headers are case insensitive
    myHeader1: string;
}

...

@Get('/hello')
getHello(@RequestHeader(MyHeaderDTO) headers: MyHeaderDTO) {
    console.log(headers);
}

【讨论】:

  • 我这样做,正如您在控制器中看到的那样。但是,我没有发送标头,也没有失败。此外,在 request-headers.decorator.ts 中,该代码不会运行,因为 ClassType 未定义。确实返回标头的答案不会调用验证,这仍然是我的主要问题。
  • @StevenScott 在你的例子中你做@RequestHeader() HeaderInfo: HeadersDTO,它应该是@RequestHeader(HeadersDTO) headers: HeadersDTO
  • 我添加了@RequestHeader(HeadersDTO) headers: HeadersDTO,但这并没有发生故障。例如,使用 Postman 发布请求,我禁止发送用户代理。我的代码检查它是我们的客户。我用示例 DTO 更新了问题。
猜你喜欢
  • 2020-03-09
  • 2019-11-30
  • 2015-12-04
  • 1970-01-01
  • 2022-01-19
  • 2014-04-06
  • 1970-01-01
  • 1970-01-01
  • 2020-11-29
相关资源
最近更新 更多