class-validator校验Polygon三维坐标数组报错排查
三维坐标数组校验失败问题
请求载荷
{ "name": { "ar": "مصر", "en": "EG" }, "location": { "type": "Polygon", "coordinates": [ [ [30.7939, 26.8206], [32.1597, 26.8206], [32.1597, 23.6345], [30.7939, 23.6345], [30.7939, 26.8206] ] ] }, "addressesType": "Country" }
需求
coordinates需为三维数组,最内层每个数组恰好包含2个数字,分别代表经度和纬度。
当前校验代码
/* eslint-disable lines-between-class-members */ /* eslint-disable @darraghor/nestjs-typed/validated-non-primitive-property-needs-type-decorator */ import { ArrayMaxSize, ArrayMinSize, ArrayNotEmpty, Equals, IsArray, IsNumber, IsOptional, IsString, ValidateNested } from 'class-validator'; export default class AddressLocationDto { @IsOptional() @IsString() @Equals('Polygon') type: string; @IsArray() @ArrayNotEmpty() @ArrayMinSize(1) @ArrayMaxSize(1) @ValidateNested({ each: true }) @IsArray() @ArrayNotEmpty() @ArrayMinSize(1) @ValidateNested({ each: true }) @IsArray() @ArrayNotEmpty() @ArrayMinSize(2) @ArrayMaxSize(2) @IsNumber({}, { each: true }) coordinates: number[][][]; }
错误信息(中文翻译)
coordinates的每个元素必须是类实例或普通对象,但收到的是数组。
问题原因
直接在coordinates属性上叠加多层数组校验装饰器的方式错误,且滥用了@ValidateNested——该装饰器用于验证嵌套的对象/类实例,而非嵌套数组。装饰器会被依次应用于当前属性,而非逐层作用于数组的嵌套层级,导致校验逻辑混乱。
解决方案
方案1:嵌套DTO类(推荐,符合class-validator最佳实践)
通过拆分嵌套DTO类,逐层校验数组结构:
/* eslint-disable lines-between-class-members */ /* eslint-disable @darraghor/nestjs-typed/validated-non-primitive-property-needs-type-decorator */ import { ArrayMaxSize, ArrayMinSize, ArrayNotEmpty, Equals, IsArray, IsNumber, IsOptional, IsString, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; // 最内层:经纬度数组校验 class CoordinateDto { @IsArray() @ArrayMinSize(2) @ArrayMaxSize(2) @IsNumber({}, { each: true }) readonly value: number[]; } // 中间层:多边形环的坐标点数组校验 class PolygonRingDto { @IsArray() @ArrayNotEmpty() @ValidateNested({ each: true }) @Type(() => CoordinateDto) readonly coordinates: CoordinateDto[]; } export default class AddressLocationDto { @IsOptional() @IsString() @Equals('Polygon') type: string; @IsArray() @ArrayNotEmpty() @ArrayMinSize(1) @ArrayMaxSize(1) @ValidateNested({ each: true }) @Type(() => PolygonRingDto) coordinates: PolygonRingDto[]; }
方案2:自定义验证器(简洁高效)
编写自定义验证器直接校验三维数组结构,无需拆分嵌套类:
/* eslint-disable lines-between-class-members */ /* eslint-disable @darraghor/nestjs-typed/validated-non-primitive-property-needs-type-decorator */ import { ArrayMaxSize, ArrayMinSize, ArrayNotEmpty, Equals, IsArray, IsNumber, IsOptional, IsString, registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator'; // 自定义验证器:校验多边形坐标的三维数组结构 function IsValidPolygonCoordinates(validationOptions?: ValidationOptions) { return function (object: Object, propertyName: string) { registerDecorator({ name: 'isValidPolygonCoordinates', target: object.constructor, propertyName: propertyName, options: validationOptions, validator: { validate(value: any) { // 第一层:必须是长度为1的数组 if (!Array.isArray(value) || value.length !== 1) return false; const ring = value[0]; // 第二层:必须是非空数组 if (!Array.isArray(ring) || ring.length === 0) return false; // 最内层:每个元素必须是长度为2的数字数组 return ring.every(point => Array.isArray(point) && point.length === 2 && point.every(num => typeof num === 'number') ); }, defaultMessage() { return 'coordinates必须是三维数组,最内层每个数组需包含2个数字(经度、纬度)'; } } }); }; } export default class AddressLocationDto { @IsOptional() @IsString() @Equals('Polygon') type: string; @IsValidPolygonCoordinates() coordinates: number[][][]; }
内容的提问来源于stack exchange,提问作者Ali Tarek
相关产品推荐
相关产品推荐

