跳到主要内容
版本:4.0.0 🚧

管道

管道是参数装饰器的内部机制,可以在参数装饰器逻辑之后执行一些自定义代码,一般用于以下的场景:

  • 1、数据的校验
  • 2、参数的转换

组件提供的管道

@midwayjs/validate 默认提供了验证管道,只需要启用组件即可使用。

例如:

@Controller('/api/user')
export class HomeController {

@Post('/')
async updateUser(@Body() user: UserDTO ) {
// ...
}
}

@Body 装饰器已经被自动注册了 ValidatePipe ,如果 UserDTO 是一个已经经过 @Rule 装饰器修饰的 DTO,会自动校验并转换。

如果使用了基础类型,则也可以通过数据转换管道进行校验和转换。

例如:

import { ParseIntPipe } from '@midwayjs/validate';

@Controller('/api/user')
export class HomeController {

@Post('/update_age')
async updateAge(@Body('age', [ParseIntPipe]) age: number ) {
// ...
}
}

ParseIntPipe 管道可以将字符串,数字数据转换为数字,这样从请求参数获取到的 age 字段则会通过管道的校验并转换为数字格式。

除此之外,还提供了 ParseBoolPipeParseFloatPipe 等更多数据转换管道,具体请查看 Validate 组件

自定义管道

管道可以是一个实现 PipeTransform 接口的类或者方法,我们一般将管道放在 src/pipe 目录。

比如:

// src/pipe/validate.pipe.ts
import { Pipe, PipeTransform, TransformOptions } from '@midwayjs/core';

@Pipe()
export class ValidatePipe implements PipeTransform<T, R> {
transform(value: T, options: TransformOptions): R {
return value;
}
}

PipeTransform<T, R> 是每个管道必须要实现的泛型接口。泛型 T 表明输入的 value 的类型,R 表明 transfrom() 方法的返回类型。

为实现 PipeTransfrom,每个管道必须声明 transfrom() 方法。该方法有两个参数:

  • value
  • options

value 是当前处理的参数值,options 是当前处理的选项,包含以下属性。

export TransformOptions<OriginType = unknown> {
metaType: TSDesignType<OriginType>;
metadata: Record<string, any>;
target: any;
methodName: string;
}
参数描述
metaType一个 ts 元数据类型的解析对象,包含 nameoriginDesignisBaseType 三个属性。
metadata参数装饰器的元数据对象
target当前装饰的实例本身
methodName当前参数装饰器装饰器的方法名

绑定管道

管道必须依附在参数装饰器上使用。

在自定义装饰器的选项中,我们可以透传管道参数达到应用管道的目的。

例如我们自定义一个 RegValid 参数装饰器,用于传入正则和另一个管道参数:

import { PipeUnionTransform, createCustomParamDecorator } from '@midwayjs/core';

function RegValid(reg: RegExp, pipe: PipeUnionTransform) {
return createCustomParamDecorator('reg-valid', {
reg,
}, {
// ...
pipes: [pipe]
});
}

createCustomParamDecorator 的第三个参数支持传入一个 pipes 属性,我们需要将管道传入其中,这样管道就会和装饰器绑定,在后续的运行中自动执行。

具体可以查询 自定义装饰器 中的参数装饰器章节。

RegValid 装饰器用于正则的校验,实现部分我们暂时忽略。

另外,我们再定义一个管道用于截取数据。

@Pipe()
export class CutPipe implements PipeTransform {
transform(value: number, options: TransformOptions): string {
return String(value).slice(5);
}
}

现在我们可以使用他们了。

class UserService {
async invoke(@RegValid(/\d{11}/, CutPipe) phoneNumber: string) {
return phoneNumber;
}
}

invoke(13712345678) => '345678'

默认绑定的管道

假如我们希望向一个现成的参数装饰器能拥有管道能力,但是不希望该装饰器有管道参数。

就像内置的 @Query 等装饰器,没有管道参数,却可以在 validate 组件启用时自动执行管道逻辑。

我们使用 decoratorService 提供的反向注册 API,这在跨组件提供能力时非常有用。

我们以上面编写的 RegValid 为例。

@Configuration({
// ...
})
export class MainConfiguration {
@Inject()
decoratorService: MidwayDecoratorService;

async onReady(container: IMidwayContainer) {
// register default pipe
this.decoratorService.registerParameterPipes('reg-valid', [
CutPipe,
]);
}
}

registerParameterPipes 方法用于向某一种参数装饰器隐式注册一些管道,上述实例中,reg-valid 是自定义参数的 key,通过 key 我们可以向这个参数装饰器注册。

这些管道会在显式传入的管道之前被默认执行。

这样在使用时,即使我们不传递管道参数,也依旧会执行管道。

class UserService {
async invoke(@RegValid(/\d{11}/) phoneNumber: string) {
return phoneNumber;
}
}

invoke(13712345678) => '345678'