Skip to content

过滤器

作用

主要用于捕获异常,一般用于全局

默认

bash
{
  "statusCode": 500,
  "message": "Internal server error"
}

过滤器使用

创建一个过滤器

ts
import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from "@nestjs/common";
import { Request, Response } from "express";

@Catch(HttpException)
export class GlobalExceptionFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    // 状态码
    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;
    const message =
      exception instanceof HttpException ? exception.message : exception.stack;
    response.status(status).json({
      code: status,
      timestamp: new Date().toLocaleString(),
      path: request.url,
      message: message,
    });

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

挂载到全局

app.modules.ts 中 找到

providers 里面改变 providers 对应的就是名字

useClass 对应的就是你要改变的那个类名

ts
import { Module } from "@nestjs/common";
import { LoginModule } from "./Login/login.module";
// 过滤器
import { GlobalExceptionFilter } from "./Global/filter/global.filter";
import { APP_FILTER } from "@nestjs/core";
@Module({
  imports: [LoginModule],
  controllers: [],
  providers: [
    {
      provide: APP_FILTER,
      useClass: GlobalExceptionFilter,
    },
  ],
})
export class AppModule {}

挂载到单个

  • 找到你要找到的路由文件,直接挂载
ts
import {
  Body,
  Controller,
  Delete,
  Get,
  HttpException,
  Inject,
  Param,
  Patch,
  Post,
  UseFilters,
} from "@nestjs/common";
import { LoginService } from "../service/login.service";
import { CustomExceptionFilter } from "../../Global/filter/customfilter";
@Controller("logins")
export class LoginController {
  @Inject()
  private loginService: LoginService;

  constructor() {}

  @Get()
  findAll() {
    return this.loginService.findAll();
  }

  @Get("test")
  @UseFilters(CustomExceptionFilter)
  findAlltest() {
    throw new HttpException("内部错误", 402);
  }
}

自定义异常

顾名思义 就是我自己手动定义一个异常.然后在定义一个过滤器单独用来捕捉它

一般用在单独的模块中,比如参数验证,用户验证等等

我下面就用管道来做个说明

1.先创建一个自定义异常类

  • globalcheckparams.exception.ts
ts
export class GlobalCheckParamsException {
  message: string;
  constructor(message?) {
    this.message = message;
  }
}

2.创建一个过滤器

这个过滤器就用来捕捉我这个异常

  • globalcheck.filter.ts
ts
import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpStatus,
} from "@nestjs/common";

import { GlobalCheckParamsException } from "./globalcheckparams.exception";

@Catch(GlobalCheckParamsException)
export class GlobalCheckExceptionsFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();
    // 我这里设置死了 200 方便前端不用飘红
    const status = 200;
    // 错误内容
    const message =
      exception instanceof GlobalCheckParamsException
        ? exception.message
        : exception.stack;
    // 返回
    response.status(status).json({
      code: 403,
      timestamp: new Date().toLocaleString(),
      path: request.url,
      data: message,
      message: message,
      name: "验证拦截器",
    });
  }
}

3. 把这个过滤器挂载到全局

  • app.module.ts
ts
import { GlobalCheckExceptionsFilter } from "./globalcheck.filter";
import { GlobalPipe } from "./global.pipe";
@Module({
  imports: [UserModule],
  controllers: [],
  providers: [
    {
      provide: APP_FILTER,
      useClass: GlobalCheckExceptionsFilter,
    },
    {
      provide: APP_PIPE,
      useClass: GlobalPipe,
    },
  ],
})
export class AppModule {}

4.使用

  • 全局管道 global.pipe 验证参数类型
ts
import { PipeTransform, Injectable, ArgumentMetadata } from "@nestjs/common";
import { plainToInstance } from "class-transformer";
import { validate } from "class-validator";
// 引入我自己验证的异常类
import { GlobalCheckParamsException } from "./globalcheckparams.exception";
@Injectable()
export class GlobalPipe implements PipeTransform {
  // 校验类型
  private toValidate(metatype: Function): boolean {
    // 其他类型不验证
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
  async transform(value: any, metadata: ArgumentMetadata) {
    const { metatype } = metadata;

    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    // 变成对象,把规则和值 组合成验证的对象
    const object = plainToInstance(metatype, value);
    // 验证 errors 是个数组
    const errors = await validate(object);

    if (errors.length > 0) {
      // 第一种只返回第一个
      if (errors.length == 1) {
        for (let arr in errors[0].constraints) {
          throw new GlobalCheckParamsException(`${errors[0].constraints[arr]}`);
        }
      }

      // 第二种返回所有
      let result: { message: string; field: string }[] = [];
      errors.forEach((item) => {
        for (let arr in item.constraints) {
          result.push({
            message: item.constraints[arr],
            field: item.property,
          });
        }
      });
      throw new GlobalCheckParamsException(JSON.stringify(result));
    }
  }
}

注意

注意

  1. 这样每次抛出异常出来,会自动给你转化成 json 格式
  2. 要是绑定到路由上面.它只会总路由上面的过滤器,全局的它不在走了