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 } from "@nestjs/common";

import { GlobalCheckException } from "./globalcheck.exception";

@Catch(GlobalCheckException)
export class GlobalExceptionsCheckFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const status = 200;

    // 错误内容
    const message =
      exception instanceof GlobalCheckException
        ? exception.message
        : exception.stack;

    response.status(status).json({
      code: 403,
      data: JSON.parse(message),
      message: "操作失败",
    });
  }
}
  • global.filter(全局异常过滤器)
ts
import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
} from "@nestjs/common";

@Catch()
export class GlobalExceptionsFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const message =
      exception instanceof HttpException ? exception.message : exception.stack;

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      data: message,
    });
  }
}

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

  • app.module.ts
ts
import { GlobalCheckExceptionsFilter } from "./globalcheck.filter";
import { GlobalExceptionsFilter } from "./common/global.filter";
import { GlobalPipe } from "./global.pipe";
@Module({
  imports: [UserModule],
  controllers: [],
  providers: [
    // 全局异常过滤器(他负责处理其他异常)
    {
      provide: APP_FILTER,
      useClass: GlobalExceptionsFilter,
    },
    // 参数异常
    {
      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 { GlobalCheckException } from "./globalcheck.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) {
          const result = [
            {
              message: `${errors[0].constraints[arr]}`,
              field: errors[0].property,
            },
          ];
          throw new GlobalCheckException(JSON.stringify(result));
        }
      }

      // 第二种返回所有
      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 GlobalCheckException(JSON.stringify(result));
    }
    return value;
  }
}

注意

注意

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