Pipe 经常被用来处理使用者传入的参数,比如:验证参数的正确性、型别的转换等。它有点像是客人画完点餐单之後,服务生要进行点餐单的检查。
在 Nest 中,Pipe 支援 Exception 的错误处理机制,当在 Pipe 抛出 Exception 时,该次请求就 不会 进入到 Controller 对应的方法里,这样的设计方法能够有效隔离验证程序与主执行程序,是非常好的实作方式。
Nest 内建了以下几个 Pipe 来辅助资料转型与验证:
ValidationPipe
:验证资料格式的 Pipe。ParseIntPipe
:解析并验证是否为 Integer
的 Pipe。ParseBoolPipe
:解析并验证是否为 Boolean
的 Pipe。ParseArrayPipe
:解析并验证是否为 Array
的 Pipe。ParseUUIDPipe
:解析并验证是否为 UUID 格式的 Pipe。DefaultValuePipe
:验证资料格式的 Pipe。Pipe 的使用方式很简单,假设要解析并验证路由参数是否为 Integer
的话,只需要在 @Param
装饰器填入路由参数名称并带入 ParseIntPipe
即可。以 app.controller.ts
为例,如果 id
解析後为数字,就会透过 AppService
去取得对应的 User 资讯,否则会抛出 Exception:
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get(':id')
getUser(@Param('id', ParseIntPipe) id: number) {
return this.appService.getUser(id);
}
}
调整 app.service.ts
:
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getUser(id: number) {
const users = [
{
id: 1,
name: 'HAO'
}
];
const user = users.find(x => x.id === id);
return user || {};
}
}
透过浏览器查看 http://localhost:3000/HAO 会收到错误讯息,因为路由参数为 HAO
,并不能解析为 Integer
:
{
"statusCode": 400,
"message": "Validation failed (numeric string is expected)",
"error": "Bad Request"
}
假设想要更改错误讯息,那 ParseIntPipe
就必须实例化并带入相关参数,以 app.controller.ts
为例,我希望出错时收到的 HttpCode 是 406
:
import { Controller, Get, HttpStatus, Param, ParseIntPipe } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get(':id')
getUser(
@Param('id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }))
id: number
) {
return this.appService.getUser(id);
}
}
透过浏览器查看 http://localhost:3000/HAO 会得到下方结果:
{
"statusCode": 406,
"message": "Validation failed (numeric string is expected)",
"error": "Not Acceptable"
}
如果想要自订错误讯息的话,可以使用 exceptionFactory
这个参数来指定产生的 Exception。以 app.controller.ts
为例:
import { Controller, Get, NotAcceptableException, Param, ParseIntPipe } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get(':id')
getUser(
@Param(
'id',
new ParseIntPipe({
exceptionFactory: () => new NotAcceptableException('无法解析为数字')
})
)
id: number
) {
return this.appService.getUser(id);
}
}
透过浏览器查看 http://localhost:3000/HAO 会得到下方结果:
{
"statusCode": 406,
"message": "无法解析为数字",
"error": "Not Acceptable"
}
如果觉得内建的 Pipe 无法满足需求的话,Nest 是可以自订 Pipe 的,事实上,Pipe 就是一个带有 @Injectable
的 class
,不过它要去实作 PipeTransform
这个介面。Pipe 可以透过 CLI 产生:
$ nest generate pipe <PIPE_NAME>
注意:
<PIPE_NAME>
可以含有路径,如:pipes/parse-int
,这样就会在src
资料夹下建立该路径并含有 Pipe。
这边我建立一个 ParseIntPipe
在 pipes
资料夹下:
$ nest generate pipe pipes/parse-int
在 src
底下会看见一个名为 pipes
的资料夹,里面有 parse-int.pipe.ts
以及 parse-int.pipe.spec.ts
:
下方为 Pipe 的骨架,会看到有一个 transform(value: any, metadata: ArgumentMetadata)
方法,这就是要做逻辑判断的地方,其中,value
为传进来的值,metadata
为当前正在处理的参数元数据:
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
return value;
}
}
注意:
PipeTransform
後面可以添加两个 Type,第一个为T
,定义传入的值应该为何种型别,也就是transform
里面的value
,第二个为R
,定义回传的资料型别。
这里我们调整一下 parse-int.pipe.ts
,经过 parseInt
之後的 value
是否为 NaN
,如果是则会抛出 NotAcceptableException
:
import { ArgumentMetadata, Injectable, NotAcceptableException, PipeTransform } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata) {
const integer = parseInt(value);
if ( isNaN(integer) ) {
throw new NotAcceptableException('无法解析为数字');
}
return integer;
}
}
接着去修改 app.controller.ts
,来套用看看自己设计的 ParseIntPipe
:
import { Controller, Get, Param } from '@nestjs/common';
import { AppService } from './app.service';
import { ParseIntPipe } from './pipes/parse-int.pipe';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get(':id')
getUser(
@Param('id', ParseIntPipe) id: number
) {
return this.appService.getUser(id)
}
}
透过浏览器查看 http://localhost:3000/HAO 会得到下方结果:
{
"statusCode": 406,
"message": "无法解析为数字",
"error": "Not Acceptable"
}
Pipe 在资料验证这块是非常实用的功能,不过如果有物件类型的资料要如何验证呢?这部分我留到下篇再详细说明。附上今天的懒人包:
@Injectable
的 class
,它要去实作 PipeTransform
这个介面。
JAVA - Windows 10 安装 Maven 参考资料 参考:(一)maven 新手教学: ...
今天的目标是:用NodeJS练习写一个简单的Web Server,所以会先介绍一下NodeJS中的h...
Django ORM介绍 一般而言,我们要存取资料库需要透过SQL语法,但在django则是使用OR...
加权指数 完成W底後,在10/19站上颈线,直接一路狂奔直到11/19,历经1个月的多头格局, 在这...
终於要从 Beginner 迈向 Intermediate 了。 这次的讲者讲话好清楚,转 1.75...