nest学习3:管道 - 守卫 - 拦截器 - 过滤器 - 中间件 - 装饰器

本文详细介绍了NestJS框架中的核心组件——管道、守卫、拦截器、过滤器和中间件的作用、执行顺序、使用方式以及注册方法。管道用于数据处理和验证,守卫则用于保护路由并验证用户登录,拦截器可拦截请求和响应以统一响应内容,过滤器用于异常捕获,而中间件常用于日志记录。文章还提供了自定义这些组件的命令和示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章问题导向

如何使用管道?
如何使用守卫?
如何使用拦截器?
如何使用过滤器?
如何使用中间件?

如果你都有了答案,可以忽略本文章,或去nest学习导图寻找更多答案


分别作用

管道:数据处理与转换,数据验证
守卫:验证用户登陆,保护路由
拦截器:对请求响应进行拦截,统一响应内容
过滤器:异常捕获
中间件:日志打印
装饰器:获取数据,实现守卫
更多使用方式,请了解执行时机再选用适用的方式

执行顺序(时机)

点击参考该文章


使用(注册)方式

两种使用方式:局部,全局
两种使用类型:内置,自定义


全局使用: 管道 - 守卫 - 拦截器 - 过滤器 - 中间件

统一在main.ts文件中使用,全局生效

import { NestFactory } from '@nestjs/core';
import { ParseIntPipe } from '@nestjs/common';
import { AppModule } from './app.module';

import { HttpExceptionFilter } from './common/filters/http-exception.filter';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { AuthGuard } from './common/guard/auth.guard';
import { AuthInterceptor } from './common/interceptors/auth.interceptor';


async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  //全局使用管道:这里使用的是内置,也可以使用自定义管道,在下文
  app.useGlobalPipes(new ParseIntPipe());

  //全局使用中间件
  app.use(LoggerMiddleware)
  
  //全局使用过滤器
  //这里使用的是自定义过滤器,先别管,先学会怎么在全局使用
  app.useGlobalFilters(new HttpExceptionFilter());  

  //全局使用守卫
  app.useGlobalGuards(new AuthGuard());
  
  //全局使用拦截器
  app.useGlobalInterceptors(new AuthInterceptor());
  
  await app.listen(3000);
}
bootstrap();

管道

内置管道

常用内置管道,从@nestjs/common导出

ParseIntPipe:将字符串数字转数字
ValidationPipe:验证管道
更多待更新

局部使用管道

局部有三种使用方式

  1. 匹配整个路径,使用UseFilters
  2. 只匹配某个接口,使用UseFilters
  3. 在获取参数时匹配,一般使用内置管道

例子

import {
  Controller,
  Get,
  Put,
  Body,
  Param,
  UsePipes,
  ParseIntPipe
} from '@nestjs/common';
import { myPipe } from '../../common/pipes/user.pipe';

@Controller('user')
@UsePipes(new myPipe())  //局部方式1:匹配整个/user, get请求和put请求都会命中
export class UserController {
  @Get(':id')
  getUserById(@Param('id', new ParseIntPipe()) id) { //局部方式3:只匹配/user的get请求,使用的是内置管道
    console.log('user', typeof id);
    return id;
  }

  @Put(':id')
  @UsePipes(new myPipe())  //局部方式2:只匹配/user的put请求
  updateUser(@Body() user, @Param('id') id) {
    return {
      user,
      id,
    };
  }
}


自定义管道

使用快捷命令生成:nest g pi myPipe common/pipes

import {
  ArgumentMetadata,
  Injectable,
  PipeTransform,
  BadRequestException,
} from '@nestjs/common';

//自定义管道必须实现自PipeTransform,固定写法,该接口有一个transform方法
//transform参数:
//value:使用myPipe时所传递的值,可以是param传递的的查询路径参数,可以是body的请求体
//metadata:元数据,可以用它判断是来自param或body或query
@Injectable()
export class myPipe implements PipeTransform<string> {
  transform(value: string, metadata: ArgumentMetadata) {
    if (metadata.type === 'body') {
      console.log('来自请求体', value);
    }
    if (metadata.type === 'param') {
      console.log('来自查询路径', value);

      const val = parseInt(value, 10);
      //如果不是传递一个数字,抛出错误
      if (isNaN(val)) {
        throw new BadRequestException('Validation failed');
      }
      return val;
    }
    return value;
  }
}

中间件

局部使用中间件

注意: 需要在使用模块的module中注册

import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middlerware';
import { UserModule } from './modules/user/user.module';

@Module({
	imports:[ UserModule ]
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware) //应用中间件
      .exclude({ path: 'user', method: RequestMethod.POST })  //排除user的post方法
      .forRoutes('user'); //监听路径  参数:路径名或*,*是匹配所以的路由
      // .forRoutes({ path: 'user', method: RequestMethod.POST }, { path: 'album', method: RequestMethod.ALL }); //多个
     // .apply(UserMiddleware) //支持多个中间件
     // .forRoutes('user')
  }
}

自定义中间件

使用快捷命令生成:nest g mi logger common/middleware

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  //req:请求参数
  //res:响应参数
  //next:执行下一个中件间
  use(req: Request, res: Response, next: () => void) {
    const { method, path } = req;
    console.log(`${method} ${path}`);
    next();
  }
}

过滤器

局部使用过滤器
import {
  Controller,
  Get,
  UseFilters,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { HttpExceptionFilter } from '../../common/filters/http-exception.filter';

//局部使用过滤器
@UseFilters(new HttpExceptionFilter())
@Controller('/user')
export class ExceptionController {
  @Get()
  getUserById(@Query() { id }): string {
    if (!id) {
      throw new HttpException(
        {
          status: HttpStatus.BAD_REQUEST,
          message: '请求参数id 必传',
          error: 'id is required',
        },
        HttpStatus.BAD_REQUEST,
      );
    }
    return 'hello error';
  }
}

自定义过滤器

使用快捷命令生成:nest g f myFilter common/filters

import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';

//必须实现至ExceptionFilter,固定写法,该接口只有一个catch方法
//catch方法参数:
//exception:当前正在处理的异常对象
//host:传递给原始处理程序的参数的一个包装(Response/Request)的引用
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const status = exception.getStatus(); //获取状态码
    const exceptionRes: any = exception.getResponse(); //获取响应对象
    const { error, message } = exceptionRes;

    //自定义的异常响应内容
    const msgLog = {
      status,
      timestamp: new Date().toISOString(),
      path: request.url,
      error,
      message,
    };

    response.status(status).json(msgLog);
  }
}

守卫

自定义守卫

使用快捷命令生成:nest g gu myGuard common/guards

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core'; //反射器,作用与自定义装饰器桥接,获取数据

//自定义守卫必须CanActivate,固定写法,该接口只有一个canActivate方法
//canActivate参数:
//context:请求的(Response/Request)的引用
//通过守卫返回true,否则返回false,返回403状态码
@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly reflector: Reflector) { }

  // 白名单数组
  private whiteUrlList: string[] = ['/user'];

  // 验证该次请求是否为白名单内的路由
  private isWhiteUrl(urlList: string[], url: string): boolean {
    if (urlList.includes(url)) {
      return true;
    }
    return false;
  }

  canActivate(context: ExecutionContext): boolean {
    // 获取请求对象
    const request = context.switchToHttp().getRequest();
    //console.log('request', request.headers);
    //console.log('request', request.params);
    //console.log('request', request.query);
    //console.log('request', request.url);

    // 用法一:验证是否是白名单内的路由
    if (this.isWhiteUrl(this.whiteUrlList, request.url)) {
      return true;
    } else {
      return false;
    }

    // 用法二:使用反射器,配合装饰器使用,获取装饰器传递过来的数据
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    //console.log(roles); // [ 'admin' ]
    //http://localhost:3000/user/9?user=admin,如果与装饰器传递过来的值匹配则通过,否则不通过
    //真实开发中可能从cookie或token中获取值
    const { user } = request.query;
    if (roles.includes(user)) {
      return true;
    } else {
      return false;
    }

    // 其他用法
    // 获取请求头中的token字段
    const token = context.switchToRpc().getData().headers.token;
    // console.log('token', token);

    // 获取session
    const userinfo = context.switchToHttp().getRequest().session;
    // console.log('session', userinfo);

    return true;
  }
}

自定义装饰器

自定义守卫中使用到了自定义装饰器

nest g d role common/decorator

//这是快捷生成的代码

import { SetMetadata } from '@nestjs/common';

//SetMetadata作用:将获取到的值,设置到元数据中,然后守卫通过反射器才能获取到值
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
局部使用守卫
import {
  Controller,
  Get,
  Delete,
  Param,
  UsePipes,
  UseGuards,
  ParseIntPipe,
} from '@nestjs/common';
import { AuthGuard } from '../../common/guard/auth.guard';
import { Role } from '../../common/decorator/role.decorator'; //自定义装饰器

@UseGuards(AuthGuard) //局部使用守卫,守卫整个user路径
@Controller('user')
export class UserController {
  @Get(':id')
  getUserById(@Param('id', new ParseIntPipe()) id) {
    console.log('user', typeof id);
    return id;
  }

  @Delete(':id')
  @Role('admin')  //使用自定义装饰器,传入角色,必须是admin才能删除
  removeUser(@Param('id') id) {
    return id;
  }
}

拦截器

自定义拦截器

使用快捷命令生成:nest g in auth common/intercepters

在全局使用

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';

//自定义拦截器必须实现自NestInterceptor,固定写法,该接口只有一个intercept方法
//intercept参数:
//context:请求上下文,可以拿到的Response和Request
@Injectable()
export class AuthInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    console.log('拦截器', request.url);
    return next.handle().pipe(
      map((data) => {
        console.log('全局响应拦截器方法返回内容后...');
        return {
          status: 200,
          timestamp: new Date().toISOString(),
          path: request.url,
          message: '请求成功',
          data: data,
        };
      }),
    );
  }
}

学习更多

nest学习导图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值