Skip to content

NEST 全局九件套

九件套架构

  • 新建一个 src/common 文件夹
bash

── src
   ├── app.module.ts
   ├── common
   ├── globalcheck.exception.ts 管道抛出的异常
   ├── globalguard.exception.ts 守卫抛出的异常
   ├── global_check.filter.ts 捕捉管道抛出的异常
   ├── global_guard_check.filter.ts 捕捉守卫抛出的异常
   ├── global_unauth.filter.ts 捕捉登录策略抛出的异常
   ├── global.filter.ts 全局过滤器 捕捉其他全局异常
   ├── global.guard.ts 全局守卫 验证jwt 等等
   ├── global.interceptor.ts 全局拦截器 返回格式化数据
   └── global.pipe.ts 全局管道 验证参数等等
   ├── main.ts

globalcheck.exception.ts

ts
export class GlobalCheckException {
  message: string;
  constructor(message: string) {
    this.message = message;
  }
}

globalguard.exception.ts

ts
export class GlobalGuardException {
  message: string;
  constructor(message: string) {
    this.message = message;
  }
}

global_check.filter.ts

  • 管道使用 一般用于参数验证,或者判断用户权限 code 403
ts
import { ExceptionFilter, Catch, ArgumentsHost, Inject } from "@nestjs/common";

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

// 增加日志模块
import { Logger } from "winston";
import { getReqMainInfo } from "../utils/tools";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";

@Catch(GlobalCheckException)
export class GlobalExceptionsCheckFilter implements ExceptionFilter {
  constructor(
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
  ) {}
  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;
    // 记录日志(错误消息,错误码,请求信息等)
    this.logger.error(message, {
      status: 403,
      req: getReqMainInfo(request),
      // stack: exception.stack,
    });
    response.status(status).json({
      code: 403,
      data: message,
      message: "操作失败",
    });
  }
}

global_guard_check.filter.ts

  • 守卫使用 一般用于 token 验证,code 401
ts
import { ExceptionFilter, Catch, ArgumentsHost, Inject } from "@nestjs/common";

import { GlobalGuardException } from "./globalguard.exception";

// 增加日志模块
import { Logger } from "winston";
import { getReqMainInfo } from "../utils/tools";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";

@Catch(GlobalGuardException)
export class GlobalGuardCheckFilter implements ExceptionFilter {
  constructor(
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
  ) {}
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();
    const status = 200;
    // 错误内容
    const message =
      exception instanceof GlobalGuardException
        ? exception.message
        : exception.stack;

    // 记录日志(错误消息,错误码,请求信息等)
    this.logger.error(message, {
      status: 401,
      req: getReqMainInfo(request),
      // stack: exception.stack,
    });
    response.status(status).json({
      code: 401,
      data: message,
      message: "操作失败",
    });
  }
}

global_unauth.filter.ts

ts
import { ExceptionFilter, Catch, ArgumentsHost, Inject } from "@nestjs/common";

import { Injectable, UnauthorizedException } from "@nestjs/common";

// 增加日志模块
import { Logger } from "winston";
import { getReqMainInfo } from "../utils/tools";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";

@Catch(UnauthorizedException)
export class UnauthorizedExceptionFilter implements ExceptionFilter {
  constructor(
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
  ) {}
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const status = 200;

    // 依据你自己的路由 来写错误信息
    let message = "";
    if (request.url == "/user/login") {
      message = "用户名或密码不能为空";
    } else {
      message = "token无效";
    }

    // 记录日志(错误消息,错误码,请求信息等)
    this.logger.error(message, {
      status: 401,
      req: getReqMainInfo(request),
      // stack: exception.stack,
    });
    response.status(status).json({
      code: 401,
      data: message,
      message: "操作失败",
    });
  }
}

global.filter.ts

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

// 增加日志模块
import { Logger } from "winston";
import { getReqMainInfo } from "../utils/tools";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";

@Catch()
export class GlobalExceptionsFilter implements ExceptionFilter {
  constructor(
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
  ) {}

  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;

    // 记录日志(错误消息,错误码,请求信息等)
    this.logger.error(message, {
      status: status,
      req: getReqMainInfo(request),
      // stack: exception.stack,
    });

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message: message,
      name: "全局拦截器",
    });
  }
}

global.guard.ts(yml 版本)

  • 主要验证 token jwt
ts
import {
  CanActivate,
  ExecutionContext,
  Inject,
  Injectable,
} from "@nestjs/common";
import { GlobalGuardException } from "./globalguard.exception";
import { ConfigService } from "@nestjs/config";
import { JwtServiceAll } from "../modules/jwt/jwt.service";
@Injectable()
export class GlobalGuard implements CanActivate {
  @Inject()
  private configService: ConfigService;
  @Inject()
  private jwtService: JwtServiceAll;
  async canActivate(context: ExecutionContext): Promise<boolean> {
    let isPublic = false;
    //权限验证
    const request = context.switchToHttp().getRequest();
    // 白名单
    const whiteList = this.configService.get("perm").router.whiteList;
    // 如果没有就放行
    if (whiteList.length == 0) {
      isPublic = true;
    } else {
      // 验证
      let item = { path: request.url, method: request.method };
      isPublic = whiteList.some((content) => {
        if (content.path === item.path && content.method === item.method) {
          return true;
        }
      });
    }
    if (isPublic) {
      return true;
    } else {
      // 判断
      const authorization = request.header("Authorization") || "";

      const bearer = authorization.split(" ");

      if (!bearer || bearer.length < 2) {
        throw new GlobalGuardException("登录token错误");
      }

      const token = bearer[1];
      try {
        const info = this.jwtService.verifyToken(token);
        // 我这里是解析出来后把userId赋值给request 这样控制器就拿到了
        (request as any).userId = info.userId; // 换成你自己的payload对应字段
        return true;
      } catch (e) {
        const result = "token 错误";
        throw new GlobalGuardException(result);
      }
    }
  }
}

global.guard.ts(env 版本)

  • 主要验证 token jwt
ts
import {
  CanActivate,
  ExecutionContext,
  Inject,
  Injectable,
} from "@nestjs/common";
import { GlobalGuardException } from "./globalguard.exception";
import { ConfigService } from "@nestjs/config";
import { JwtAllService } from "../moduels/jwt/jwt.service";
@Injectable()
export class GlobalGuard implements CanActivate {
  @Inject()
  private configService: ConfigService;
  @Inject()
  private jwtService: JwtAllService;
  async canActivate(context: ExecutionContext): Promise<boolean> {
    let isPublic = false;
    //权限验证
    const request = context.switchToHttp().getRequest();
    // 白名单
    const whiteList = this.configService.get("PERMISSION_WHITELIST");
    // 如果没有就放行
    if (whiteList == "" || whiteList.includes(request.url)) {
      isPublic = true;
    }
    if (isPublic) {
      return true;
    } else {
      // 判断
      const authorization = request.header("Authorization") || "";

      const bearer = authorization.split(" ");

      if (!bearer || bearer.length < 2) {
        throw new GlobalGuardException("登录token错误");
      }

      const token = bearer[1];
      try {
        const info = this.jwtService.verifyToken(token);
        // 我这里是解析出来后把userId赋值给request 这样控制器就拿到了
        (request as any).userId = info.userId; // 换成你自己的payload对应字段
        return true;
      } catch (e) {
        const result = "token 错误";
        throw new GlobalGuardException(result);
      }
    }
  }
}

global.interceptor.ts

  • 全局拦截器(env 版本)
ts
/*
 * @Author: jsopy
 * @Date: 2025-11-15 13:14:48
 * @LastEditTime: 2025-11-15 19:50:21
 * @FilePath: /demo1/src/common/global.interceptor.ts
 * @Description:
 *
 */
import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  Inject,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { tap, map } from "rxjs/operators";
import { ConfigService } from "@nestjs/config";

// 增加日志模块
import { Logger } from "winston";
import { getReqMainInfo } from "../utils/tools";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";

@Injectable()
export class GlobalInterceptor implements NestInterceptor {
  constructor(
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger
  ) {}

  @Inject()
  private configService: ConfigService;
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log("Before...");
    // 写一个白名单不拦截
    let isPublic = false;
    //权限验证
    const request = context.switchToHttp().getRequest();
    // 白名单
    const whiteList = this.configService.get("INTERCEPTOR_WHITELIST");
    // 如果没有就放行或者包含就放行
    if (whiteList == "" || whiteList.includes(request.url)) {
      isPublic = true;
    }

    if (isPublic) {
      return next.handle().pipe(
        map((data) => {
          // 记录请求日志
          this.logger.info("response", {
            responseData: data,
            req: getReqMainInfo(request),
          });
          return data;
        })
      );
    } else {
      return next.handle().pipe(
        map((data) => {
          console.log("After全局...");

          // 剩下的是全局返回
          // 控制器里面就可以返回 {code:200,data:xxx}
          let resultmessage = "";
          let resultcode = data.code || 200;
          if (resultcode == 200) {
            resultmessage = "操作成功";
          } else {
            resultmessage = "操作失败";
          }
          // 记录请求日志
          this.logger.info("response", {
            responseData: data,
            req: getReqMainInfo(request),
          });
          return {
            code: resultcode,
            message: resultmessage,
            data,
          };
        })
      );
    }
  }
}
  • 全局拦截器(ysml 版本)
ts
import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  Inject,
} from "@nestjs/common";
import { Observable } from "rxjs";
import { tap, map } from "rxjs/operators";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class GlobalInterceptor implements NestInterceptor {
  @Inject()
  private configService: ConfigService;
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log("Before...");
    // 写一个白名单不拦截
    let isPublic = false;
    //权限验证
    const request = context.switchToHttp().getRequest();
    // 白名单
    const whiteList = this.configService.get("noglobalinterceptor").router
      .whiteList;
    // 如果没有就放行
    if (whiteList.length == 0) {
      isPublic = true;
    } else {
      // 验证
      let item = { path: request.url, method: request.method };
      isPublic = whiteList.some((content) => {
        if (content.path === item.path && content.method === item.method) {
          return true;
        }
      });
    }

    if (isPublic) {
      return next.handle();
    } else {
      return next.handle().pipe(
        map((data) => {
          console.log("After全局...");
          let { code, message, ...result } = data;
          // 剩下的是全局返回
          // 控制器里面就可以返回 {code:200,data:xxx}
          let resultmessage = "";
          let resultcode = code || 200;
          if (resultcode == 200) {
            resultmessage = "操作成功";
          } else {
            resultmessage = "操作失败";
          }
          console.log(result.data);
          return {
            code: resultcode,
            message: resultmessage,
            data: result.data,
          };
        })
      );
    }
  }
}

global.pipe.ts

  • 全局管道 验证参数 抛出异常
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 = `${errors[0].constraints[arr]}`;
          throw new GlobalCheckException(result);
        }
      }

      // 第二种返回所有
      let result: string[] = [];
      errors.forEach((item) => {
        for (let arr in item.constraints) {
          result.push(item.constraints[arr]);
        }
      });
      throw new GlobalCheckException(result.join("|"));
    }
    return value;
  }
}

app.module.ts

ts
import { Module } from "@nestjs/common";
// 配置文件
import { ConfigModule } from "@nestjs/config";
// 引入测试模块
import { TestprojectModule } from "./moduels/testproject/testproject.module";
import { PrismadbModule } from "./moduels/prisma/prisma.module";
import { RedisModule } from "./moduels/redis/redis.module";
import { AuthModule } from "./moduels/auth/auth.module";
import { JwtAllModule } from "./moduels/jwt/jwt.module";
// 引入九件套
import { APP_FILTER, APP_INTERCEPTOR, APP_PIPE, APP_GUARD } from "@nestjs/core";
import { GlobalExceptionsFilter } from "./common/global.filter";
import { GlobalExceptionsCheckFilter } from "./common/global_check.filter";
import { GlobalGuardCheckFilter } from "./common/global_guard_check.filter";
import { UnauthorizedExceptionFilter } from "./common/global_unauth.filter";
import { GlobalInterceptor } from "./common/global.interceptor";
import { GlobalPipe } from "./common/global.pipe";
import { GlobalGuard } from "./common/global.guard";
import { GlobalWinstonModule } from "./moduels/winston/winston.module";
// 加载配置文件,这里需要引入dotenv
import * as dotenv from "dotenv";
const envPath = `.env.${process.env.NODE_ENV || "development"}`;
@Module({
  imports: [
    // 修改配置
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: envPath,
      // 这里新增.env的文件解析
      load: [() => dotenv.config({ path: ".env" })],
    }),
    // 引入测试模块
    TestprojectModule,
    // 引入数据库模块
    PrismadbModule,
    // 引入redis模块
    RedisModule,
    // 登录策略模块
    AuthModule,
    // 引入jwt模块
    JwtAllModule,
    // 引入日志模块
    GlobalWinstonModule.forRoot(),
  ],
  controllers: [],
  providers: [
    // 全局异常过滤器(他负责兜底 处理其他异常)
    {
      provide: APP_FILTER,
      useClass: GlobalExceptionsFilter,
    },
    // 检查管道过滤器
    {
      provide: APP_FILTER,
      useClass: GlobalExceptionsCheckFilter,
    },
    // 守卫过滤器
    {
      provide: APP_FILTER,
      useClass: GlobalGuardCheckFilter,
    },
    // 策略过滤器
    {
      provide: APP_FILTER,
      useClass: UnauthorizedExceptionFilter,
    },
    // 全局统一格式拦截器
    {
      provide: APP_INTERCEPTOR,
      useClass: GlobalInterceptor,
    },
    // 全局管道
    {
      provide: APP_PIPE,
      useClass: GlobalPipe,
    },
    // 全局守卫
    {
      provide: APP_GUARD,
      useClass: GlobalGuard,
    },
  ],
})
export class AppModule {}