Nest.js从0到1搭建博客系统---中间件、守卫、管道、拦截器、异常过滤器、验证器(3)

中间件、守卫、管道、拦截器、异常过滤器、验证器

前言

Nest.js 作为一个强大的 Node.js 框架,提供了一套丰富的装饰器和帮助函数来支持 AOP 的实现。其中 Nest 的实现主要包括如下五种:

  • 中间件(Middleware)
  • 守卫(Guard)
  • 管道(Pipe)
  • 拦截器(Interceptor)
  • 安装
shell 复制代码
npm install --save rxjs xml2js class-validator class-transformer
  • rxjs 提供了一种响应式编程的方式来处理异步数据流
  • xml2js 转换xml内容变成json格式
  • class-validator、class-transformer 管道验证包和转换器

中间件

Middleware是在路由处理程序之前 调用的函数。中间件函数可以访问请求响应对象,以及应用程序的请求-响应周期中的next()中间件函数。通常,next 中间件函数由一个名为next的变量表示

中间件函数可以执行以下任务:

  • 执行任何代码

  • 对请求和响应对象进行更改

  • 结束请求-响应周期

  • 调用堆栈中的下一个中间件函数

  • 如果当前中间件函数未结束请求-响应周期,则必须调用next()将控制权传递给下一个中间件函数。否则,请求将被搁置

  • 创建logger.middleware.ts中间件

shell 复制代码
nest g mi logger --no-spec --flat
ts 复制代码
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
    use(req: Request, res: Response, next: NextFunction) {
        console.log('Logger 中间件拦截', req.header);
        // 具体内容根据需求去写
        next();
    }
}

1、局部中间件

  • demo.module.ts
ts 复制代码
import { Module, NestMiddleware, MiddlewareConsumer } from '@nestjs/common';
import { DemoService } from './demo.service';
import { DemoController } from './demo.controller';
import {LoggerMiddleware} from '@/common/middleware/logger.middleware'

@Module({
  controllers: [DemoController],
  providers: [DemoService],
  exports: [DemoService],
})
export class DemoModule implements NestMiddleware {
  configure (consumer:MiddlewareConsumer) {
   // consumer.apply(LoggerMiddleware).forRoutes('demo')
    consumer.apply(LoggerMiddleware).forRoutes({path:'/demo',method:RequestMethod.GET}) //针对 Get
  }
}

执行接口就能监听到结果l

2、全局中间件

  • main.ts
ts 复制代码
import { NestFactory } from '@nestjs/core';
import { AppModule } from '@/app.module';
function LoggerMiddleware(req, res, next: () => any) {
  console.log(req.originalUrl, '我是全局中间件LoggerMiddleware')
  next()
}
async function bootstrap() {
     const app = await NestFactory.create(AppModule);
     app.use(middleWareAll)
     await app.listen(3000);
}
bootstrap();

请求demo与user接口都监听到结果了

守卫

守卫具有单一职责 。它们根据运行时的某些条件(如权限、角色、ACL等)确定是否将处理给定请求,这通常称为授权 。授权(以及它通常与之协作的身份验证 )通常在传统的Express应用程序中由中间件处理。中间件是身份验证的不错选择,因为诸如令牌验证和将属性附加到request对象之类的事情与特定路由上下文(及其元数据)没有紧密关联

  • 创建auth.guard.ts 守卫
shell 复制代码
nest g gu auth --no-spec --flat
ts 复制代码
import { Reflector } from '@nestjs/core'
import { AuthGuard } from '@nestjs/passport'
import { ExecutionContext, ForbiddenException, Inject, Injectable, UnauthorizedException } from '@nestjs/common'
import { UserService } from '@/modules/user/user.service';
import { ALLOW_ANON } from '@/common/decorator/allow-anon.decorator'
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
    constructor(private readonly reflector: Reflector, @Inject(UserService)
    private readonly userService: UserService) {
        super()
    }
    async canActivate(ctx: ExecutionContext): Promise<boolean> {
        const allowAnon = this.reflector.getAllAndOverride<boolean>(ALLOW_ANON, [ctx.getHandler(), ctx.getClass()])
        if (allowAnon) return true
        const req = ctx.switchToHttp().getRequest()
        const accessToken = req.get('Authorization')
        if (!accessToken) throw new ForbiddenException('请先登录!')
        const UserId = this.userService.verifyToken(accessToken)
        if (!UserId) throw new UnauthorizedException('当前登录已过期,请重新登录!')
        return this.activate(ctx)

    }
    async activate(ctx: ExecutionContext): Promise<boolean> {
        return super.canActivate(ctx) as Promise<boolean>
    }
}
  • 全局注册守卫
ts 复制代码
import { Module, ValidationPipe } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './modules/user/user.module';
import { DemoModule } from './modules/demo/demo.module';
import { JwtAuthGuard } from '@/common/guard/auth.guard'

@Module({
  imports: [ConfigModule.forRoot(), UserModule, DemoModule,],
  controllers: [AppController],
  providers: [AppService, {
      // 全局权限认证
      provide: APP_GUARD,
      useClass: JwtAuthGuard,
    }],

})
export class AppModule { }

管道

管道是具有 @Injectable() 装饰器的类。管道应实现 PipeTransform 接口。

管道有两个典型的应用场景:

  • 转换:管道将输入数据转换为所需的数据输出(例如,将字符串转换为整数)
  • 验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常

Nest 自带九个开箱即用的管道,即

  • ValidationPipe
  • ParseIntPipe
  • ParseFloatPipe
  • ParseBoolPipe
  • ParseArrayPipe
  • ParseUUIDPipe
  • ParseEnumPipe
  • DefaultValuePipe
  • ParseFilePipe

他们从 @nestjs/common 包中导出。

  • 创建demo.pipe.ts管道
shell 复制代码
nest g pipe demo --no-spec --flat
ts 复制代码
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
@Injectable()
export class DemoPipe implements PipeTransform {
    transform(value: any, metadata: ArgumentMetadata) {
        //这个里面可以修改传入的值以及验证转入值的合法性
        console.log("我是处理Demo参数管道");
        return value;
    }
}
  • 全局管道注册
ts 复制代码
import { DemoPipe } from '@/common/pipe/demo.pipe';
async function bootstrap() { 
    const app = await NestFactory.create(AppModule);
    app.useGlobalPipes(new DemoPipe()); a
    wait app.listen(3000); 
} 
bootstrap();
  • 或者
ts 复制代码
import { Module } from '@nestjs/common'; 
import { APP_PIPE } from '@nestjs/core'; 
import { DemoPipe } from '@/common/pipe/demo.pipe';
@Module({ 
    providers: [ { provide: APP_PIPE, useClass: DemoPipe } ] 
})
export class AppModule {}
  • Demo控制中使用
ts 复制代码
  @Post()
  @SwaggerDoc("创建demo", "demo描述", "创建demo请求", String)
  create(@Body(new DemoPipe()) createDemoDto: CreateDemoDto) {
    return this.demoService.create(createDemoDto);
  }

拦截器

拦截器具有一系列有用的功能,这些功能受面向切面编程(AOP)技术的启发。它们可以

  • 在函数执行之前/之后绑定额外的逻辑
  • 转换从函数返回的结果
  • 转换从函数抛出的异常
  • 扩展基本函数行为
  • 根据所选条件完全重写函数 (例如, 缓存目的)
  • 创建transform.interceptor.ts 拦截器
shell 复制代码
nest g itc interceptor/transform --no-spec --flat
ts 复制代码
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from "@nestjs/common"
import { Observable } from "rxjs"
import { map } from 'rxjs/operators';

interface Response<T> {
    data: T;
}

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
    intercept(context: ExecutionContext, next: CallHandler<T>): Observable<Response<T>> | Promise<Observable<Response<T>>> {
        return next.handle().pipe(
            map((data) => ({
                code: 0, //状态码
                data,//返回数据
                pagination: { //分页
                    total: 100,
                    pageSize: 10,
                    pages: 10,
                    page: 1,
                },
                extra: {}, // 拓展
                message: 'success',// 返回结果
            })),
        );
    }
}
  • 在app.module.ts全局注册
ts 复制代码
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './modules/user/user.module';
import { DemoModule } from './modules/demo/demo.module';
import { TransformInterceptor } from '@/common/interceptor/transform.interceptor';

@Module({
  imports: [ConfigModule.forRoot(), UserModule, DemoModule,],
  controllers: [AppController],
  providers: [AppService, {
    // 全局拦截器
    provide: APP_INTERCEPTOR,
    useClass: TransformInterceptor,
  }],

})
export class AppModule { }
  • 或者main.ts注册
ts 复制代码
import { TransformInterceptor } from '@/common/interceptor/transform.interceptor';
app.useGlobalInterceptors(new TransformInterceptor())

异常过滤器

Nest框架内置了一个异常处理层,负责处理应用程序中的所有未处理异常。当一个异常没有被应用程序代码处理时,它会被这个异常处理层捕获,然后自动发送一个适当的用户友好响应。

  • 创建http.exception.filter.ts异常过滤器
shell 复制代码
nest g f http.exception --no-spec --flat
ts 复制代码
import {
    ArgumentsHost,
    Catch,
    ExceptionFilter,
    HttpException,
    HttpStatus,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
    catch(exception: HttpException, host: ArgumentsHost) {
        const request = host.switchToHttp().getRequest<Request>();
        const response = host.switchToHttp().getResponse<Response>();
        // http异常处理
        response.status(HttpStatus.NOT_FOUND).send({
            statusCode: HttpStatus.NOT_FOUND,
            timestamp: new Date().toISOString(),
            path: request.url,
            message: exception.getResponse(),
        });
    }
}
  • main.ts注册
ts 复制代码
import { HttpExceptionFilter } from '@/common/filters/exceptions/http.exception.filter';
app.useGlobalFilters(new HttpExceptionFilter())
  • 或app.module.ts注册
ts 复制代码
import { Module } from '@nestjs/common';
import {  APP_FILTER } from '@nestjs/core';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './modules/user/user.module';
import { DemoModule } from './modules/demo/demo.module';
import { HttpExceptionFilter } from '@/common/filters/exceptions/http.exception.filter';

@Module({
  imports: [ConfigModule.forRoot(), UserModule, DemoModule,],
  controllers: [AppController],
  providers: [AppService, {
      // Http异常
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },],

})
export class AppModule { }

验证器

ts 复制代码
 
import { IsNotEmpty, IsString, Length, IsIn,IsNumber } from 'class-validator'
export class CreateDemoDto {

   @IsNotEmpty()//验证是否为空
   @IsString() //是否为字符串
   @Length(1, 10)
   username: string;
   @IsNotEmpty()
   @IsNumber()
   age: number
   @IsIn([0, 1])
   status: number;
}
  • 全局注册Nest内置ValidationPipe验证管道

github

项目地址:nest-vhen-blog

相关推荐
Eric_见嘉6 天前
NestJS 🧑‍🍳 厨子必修课(九):API 文档 Swagger
前端·后端·nestjs
XiaoYu200214 天前
第3章 Nest.js拦截器
前端·ai编程·nestjs
XiaoYu200215 天前
第2章 Nest.js入门
前端·ai编程·nestjs
实习生小黄16 天前
NestJS 调试方案
后端·nestjs
当时只道寻常19 天前
NestJS 如何配置环境变量
nestjs
濮水大叔1 个月前
VonaJS是如何做到文件级别精确HMR(热更新)的?
typescript·node.js·nestjs
ovensi1 个月前
告别笨重的 ELK,拥抱轻量级 PLG:NestJS 日志监控实战指南
nestjs
ovensi1 个月前
Docker+NestJS+ELK:从零搭建全链路日志监控系统
后端·nestjs
Gogo8161 个月前
nestjs 的项目启动
nestjs