1、Nest中有什么组件
- 中间件(Middleware)
- 异常过滤器(Exception filters)
- 拦截器(Interceptors)
- 守卫(Guards)
- 管道(Pipes)
2、各组件主要用法
1、中间件
仅在调用路由处理程序之前调用中间件。可以访问响应对象,但是没有路由处理程序的结果。
中间件函数可以执行以下任务:
- 执行任何代码。
- 对请求和响应对象进行更改。(使用send函数直接返回客户端,直接中断后面的流程)
- 结束请求-响应周期。
- 调用堆栈中的下一个中间件函数。
- 如果当前的中间件函数没有结束请求-响应周期, 它必须调用
next()
将控制传递给下一个中间件函数。否则, 请求将被挂起。
适用场景:
1、比如可以使用一个中间件记录到达的请求,为请求设置HTTP头部信息,再将请求传递给下一步。
2、日志记录
3、请求监控
1、全局中间件
1、定义全局中间件:
export async function markMiddleware(req: any, res: any, next: () => void){
console.log('全局中间件 Global MarkMiddleware ');
next();
}
2、注册全局中间件
import {NestFactory} from '@nestjs/core';
import {AppModule} from './app.module';
import {markMiddleware} from "./middleware/mark.middleware";
import {GlobalHttpExceptionFilter} from "./filter/GlobalHttpExceptionFilter";
import {GlobalAllExceptionFilter} from "./filter/GlobalAllExceptionFilter";
import {GlobalLoggingInterceptor} from "./interceptor/GlobalLogging.interceptor";
import {AuthGuard} from "./guard/AuthGuard";
import {GlobalValidationPipe} from "./pipe/GlobalValidate.pipe";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new GlobalValidationPipe())
app.useGlobalGuards(new AuthGuard())
app.useGlobalInterceptors(new GlobalLoggingInterceptor())
app.useGlobalFilters(new GlobalHttpExceptionFilter())
app.useGlobalFilters(new GlobalAllExceptionFilter())
app.use(markMiddleware)
await app.listen(3001);
}
bootstrap();
2、局部中间件
1、定义控制层中间件
import {Injectable, NestMiddleware} from "@nestjs/common";
@Injectable()
export class LoggerMiddleware implements NestMiddleware{
use(req: any, res: any, next: () => void): any {
console.log('控制层中间件 LoggerMiddleware');
next();
}
}
2、注册中间件
import {MiddlewareConsumer, Module, NestModule} from '@nestjs/common';
import {AppController} from './app.controller';
import {AppService} from './app.service';
import {SharedModule} from './shared/shared.module';
import {FeatureModule} from "./feature/feature.module";
import {LoggerMiddleware} from "./middleware/logger.middleware";
@Module({
imports: [SharedModule, FeatureModule],
controllers: [AppController],
providers: [AppService],
exports:[]
})
export class AppModule implements NestModule{
configure(consumer: MiddlewareConsumer): any {
consumer.apply(LoggerMiddleware)
.forRoutes(AppController)
}
}
3、中间件执行顺序
全局绑定的中间件(有多个就按注册顺序执行) -> 模块绑定的中间件(有多个就按注册顺序执行)
2、异常过滤器
异常过滤器在路由处理程序之后和拦截器之后调用。它们是在响应到达客户端之前进行更改的最后一个位置。
适用场景:
异常过滤器主要用于异常处理,提供友好的异常提示信息
1、全局过滤器
1、定义全局过滤器1
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class GlobalHttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
console.log("全局过滤器 GlobalHttpExceptionFilter")
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
from:"GlobalHttpExceptionFilter"
});
}
}
2、定义全局过滤器2
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from '@nestjs/common';
@Catch()
export class GlobalAllExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
console.log("全局过滤器 GlobalAllExceptionFilter")
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
from:"GlobalAllExceptionFilter"
});
}
}
3、注册全局过滤器1和2
import {NestFactory} from '@nestjs/core';
import {AppModule} from './app.module';
import {markMiddleware} from "./middleware/mark.middleware";
import {GlobalHttpExceptionFilter} from "./filter/GlobalHttpExceptionFilter";
import {GlobalAllExceptionFilter} from "./filter/GlobalAllExceptionFilter";
import {GlobalLoggingInterceptor} from "./interceptor/GlobalLogging.interceptor";
import {AuthGuard} from "./guard/AuthGuard";
import {GlobalValidationPipe} from "./pipe/GlobalValidate.pipe";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new GlobalValidationPipe())
app.useGlobalGuards(new AuthGuard())
app.useGlobalInterceptors(new GlobalLoggingInterceptor())
app.useGlobalFilters(new GlobalHttpExceptionFilter())
app.useGlobalFilters(new GlobalAllExceptionFilter())
app.use(markMiddleware)
await app.listen(3001);
}
bootstrap();
4、定义多个全局过滤器的执行顺序为
执行后注册的一个
2、局部过滤器
1、定义局部过滤器ControllerAllExceptionFilter
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from '@nestjs/common';
@Catch()
export class ControllerAllExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
console.log("控制层异常过滤器 ControllerAllExceptionFilter")
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
from:"ControllerAllExceptionFilter"
});
}
}
2、定义局部过滤器ControllerHttpExceptionFilter
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from '@nestjs/common';
@Catch()
export class ControllerHttpExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
console.log("控制层异常过滤器 ControllerHttpExceptionFilter")
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
from:"ControllerHttpExceptionFilter"
});
}
}
3、注册局部过滤器
4、局部过滤器执行顺序
注册多个局部(控制层、路由层)过滤器时,谁先注册,先执行谁
3、过滤器执行顺序
1、定义多个过滤器时,只会执行一个
2、按优先级执行:路由>控制器>全局
3、多个全局控制器,后注册限制性
4、多个局部控制器,先注册先执行
5、由于是发生异常引起执行过滤器,之后的流程中断
3、拦截器
拦截器可以在调用路由处理程序之前和之后访问响应/请求。
1、功能
拦截器具有一系列有用的功能,这些功能受面向切面编程(AOP)技术的启发。它们可以:
- 在函数执行之前/之后绑定额外的逻辑
- 转换从函数返回的结果
- 转换从函数抛出的异常
- 扩展基本函数行为
2、常见应用场景
1、日志记录 :记录请求信息的日志
2、性能检测:检测方法的执行时间
1、全局拦截器
1、定义全局拦截器
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class GlobalLoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('全局拦截器 GlobalLoggingInterceptor Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`全局拦截器 After... ${Date.now() - now}ms`)),
);
}
}
2、注册全局拦截器
2、局部拦截器
1、注册局部拦截器
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('控制器层拦截器 LoggingInterceptor Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`控制器层拦截器 After... ${Date.now() - now}ms`)),
//tap() 运算符,该运算符在可观察序列的正常或异常终止时调用函数。
);
}
}
2、注册局部拦截器
3、执行顺序
1、next.handle之前:全局- >控制层->路由层
2、next.handle之后:路由层- >控制层->全局
4、守卫
适用场景
- 授权,假设用户是经过身份验证的(因此,请求头附加了一个
token
)。它将提取和验证token
,并使用提取的信息来确定请求是否可以继续。 - 角色认证,允许具有特定角色的用户访问
守卫在每个中间件之后执行,但在任何拦截器或管道之前执行。
1、全局守卫
1、定义全局守卫
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
// const request = context.switchToHttp().getRequest();
console.log("全局守卫 Global AuthGuard")
return true
}
}
2、注册全局守卫
2、局部守卫
1、定义局部守卫
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class RolesGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
console.log("控制层守卫 RolesGuard")
return true;
}
}
2、注册局部守卫
3、执行顺序
5、管道
管道有两个类型:
- 转换:管道将输入数据转换为所需的数据输出
- 验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常;
管道在异常区域内运行。这意味着当抛出异常时,它们由核心异常处理程序和应用于当前上下文的 异常过滤器 处理。当在 Pipe 中发生异常,controller 不会继续执行任何方法。
1、全局管道
1、定义全局管道
import {PipeTransform, Injectable, ArgumentMetadata, BadRequestException} from '@nestjs/common';
import {plainToClass} from "class-transformer";
import {validate} from "class-validator";
@Injectable()
export class GlobalValidationPipe implements PipeTransform {
/*
value 是当前处理的参数,而 metadata 是其元数据。元数据对象包含一些属性:
export interface ArgumentMetadata {
type: 'body' | 'query' | 'param' | 'custom';
metatype?: Type<unknown>;
data?: string;
}*/
async transform(value: any, { metatype }: ArgumentMetadata) {
console.log("全局管道 global ValidationPipe")
if (!metatype || !this.toValidate(metatype)) {
return value;
}
const object = plainToClass(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException('全局管道 Validation failed');
}
return value;
}
//当验证类型不是 JavaScript 的数据类型时,跳过验证。
private toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
2、注册全局管道
2、局部管道
1、定义局部管道
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata): number {
console.log("路由参数管道 ParseIntPipe")
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException('路由参数管道 Validation failed');
}
return val;
}
}
2、注册局部管道
3、执行顺序
3、各组件间请求链路
即一个请求的生命周期
总结
1、nest中的组件
- 中间件(Middleware)
- 异常过滤器(Exception filters)
- 拦截器(Interceptors)
- 守卫(Guards)
- 管道(Pipes)
2、请求的生命周期如下
1、中间件
全局绑定的中间件(有多个就按注册顺序执行)->模块绑定的中间件(有多个就按注册顺序执行)
2、守卫
全局守卫->控制层守卫->路由守卫
3、拦截器(next.handle之前)
全局拦截器(route.handle之前)->控制器层拦截器 (route.handle之前)->路由拦截器 (route.handle之前)
4、管道
全局管道->控制器管道->路由管道->路由参数管道
5、控制器(方法处理器)
6、服务(provider)
7、拦截器(next.handle之后)
路由拦截器(route.handle之后)->控制器拦截器 (route.handle之后)->全局拦截器 (route.handle之后)
8、异常过滤器
按优先级执行:路由>控制器>全局