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.tsglobalcheck.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 {}