Nestjs 框架学习
Nestjs
是一个用于构建高效、可扩展的 Node.js
服务端应用框架,基于 TypeScript
编写并且结合了 OOP(面向对象的编程)
、FP(函数式的编程)
、FRP(函数式响应工式编程)
的相关理念。
下面介绍Nestjs
中的一些功能设计,及用法。
# 控制器 Controller
负责处理客户端请求,及相应,用来定义请求路由。
控制器一般不做复杂的业务逻辑,这部分会放到Providers中处理。
常用装饰器:
@Controller('cats')
装饰在类上,接口请求就可以通过/cats
访问到
@Get()
装饰在类的方法上,标识请求的类型为Get
,也可以传递参数
@Put(':id')
装饰在方法上,接收接口请求PUT /cats/id
# 服务提供者 Providers
Service 一般用来处理业务相关逻辑,如数据库中的数据查询。通过控制器来调用。
装饰器:
@Injectable()
# 模块 Mudule
相关控制器和 Providers 的包装整合。用来管理程序的组织结构。
装饰器:
@Module()
注册对应的控制器和服务,装饰在类上
@Global() // 声明为全局
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService] // 导出给其他服务使用
})
export class xxModule {}
# 中间件 Middleware
中间件有两种形式,函数或者类。函数和express
中定义一样,这里说明下类的定义方式。
需要使用 @Injectable()
装饰,并且实现 NestMiddleware
接口。
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log('Request...');
next();
}
}
在Module中使用中间件,只对当前模块生效
@Module({
imports: [CatsModule],
})
export class ApplicationModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats'); // 只对cats路由生效
}
}
如果是全局,可在main.ts
中使用 app.use(LoggerMiddleware)
# 异常过滤器 Exception
定义了异常处理层,专门用来负责应用程序中未处理的异常,当控制器抛出错误后,就会被一层过滤器处理。
默认异常过滤器为HttpException
# 自定义异常
通过继承HttpException
,实现自己的异常类,并手动抛出该异常:
// 自定义异常类 ForbiddenException
export class ForbiddenException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN);
}
}
使用
@Get()
async findAll() {
// 抛出该异常
throw new ForbiddenException();
}
# 自定义异常过滤器
装饰器:
@Catch(HttpException)
: 可指定参数,表示捕获指定类型的异常,如果没有参数,捕获所以异常
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
// 捕获HttpException异常
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
// 返回自定义的错误信息
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
# 使用自定义异常过滤器
装饰器:
@UseFilters(HttpExceptionFilter)
: 指定过滤器的类
全局使用:
app.useGlobalFilters(new HttpExceptionFilter())
全局使用时,需要在模块里注册,防止无法进入依赖注入。
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class ApplicationModule {}
# 管道 Pipes
主要用途:
1、格式化输入的数据
2、验证输入参数的合法性
# 如何定义管道
使用@Injectable()
来装饰类,并且实现PipTransform
接口
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata): number {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException('Validation failed');
}
return val;
}
}
# 使用管道
装饰器:
@UsePipes()
:装饰在方法上
@Post()
@UsePipes(new ValidationPipe({ transform: true }))
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
使用在指定参数上:
@Get(':id')
async findOne(@Param('id', new ParseIntPipe()) id) {
return await this.catsService.findOne(id);
}
# 守卫 Guards
主要用途:接口请求权限,角色校验
# 如何创建守卫
定义类使用@Ingectable()
装饰,并实现CanActivate
接口
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return validateRequest(request);
}
}
# 使用守卫
通过装饰器@UseGuards()
来使用
@Controller('cats')
@UseGuards(AuthGuard)
export class CatsController {}
在方法上添加元数据,指定那些权限可以访问
@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
自定义装饰器角色装饰器
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
使用自定义装饰器
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
全局使用
app.useGlobalGuards(new AuthGuard());
并在Module里注册
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class ApplicationModule {}
# 拦截器 Interceptors
拦截器有很多用处,这些功能设计灵感都来自于面向切面编程(AOP)技术。
主要作用是可以在函数执行前,或执行后绑定一些额外的逻辑,如:
1、执行控制器逻辑前,添加缓存,如果有缓存直接返回
2、对返回的结果进行处理,如固定的返回数据格式:
{
"code": 0,
"msg": "",
"data": {}
}
# 创建拦截器
创建一个类,使用@Injectable()
装饰器,并实现NestInterceptor
接口。
如下一个例子,在调用函数前后添加日志,记录执行时长
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
# 使用拦截器
使用@UseInterceptors(LoggingInterceptor)
装饰器
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
