- 创建项目
- Nest 控制器
- nest 配置路由请求数据
- Nest 服务
- Nest 模块
- 配置静态资源
- 配置模板引擎
- Cookie 的使用
- Session 的使用
- 跨域,前缀路径、网站安全、请求限速
- 管道、守卫、拦截器、过滤器、中间件
- 一例看懂中间件、守卫、管道、异常过滤器、拦截器
- 数据验证
- 配置抽离
- 环境配置
- 文件上传与下载
- 实现图片随机验证码
- 邮件服务
- nest 基于 possport + jwt 做登陆验证
- 对数据库的密码加密:md5 和 bcryptjs
- 角色权限
- 定时任务
- 接入 Swagger 接口文档
- nest 连接 Mongodb
- typeORM 操作 Mysql 数据库
- nest 统一处理数据库操作的查询结果
- 数据库实体设计与操作
- typeorm 增删改查操作
- typeorm 使用事务的 3 种方式
- typeorm 一对一关系设计与增删改查
- typeorm 一对多和多对一关系设计与增删改查
- typeorm 多对多关系设计与增删改查
- nest 连接 Redis
- 集成 redis 实现单点登录
- Q:nestJS 注入其他依赖时为什么还需要导入其 module
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
管道、守卫、拦截器、过滤器、中间件
- 管道:数据处理与转换,数据验证
- 守卫:验证用户登陆,保护路由
- 拦截器:对请求响应进行拦截,统一响应内容
- 过滤器:异常捕获
- 中间件:日志打印
执行顺序(时机)
从客户端发送一个 post 请求,路径为: /user/login
,请求参数为: {userinfo: ‘xx’,password: ‘xx’}
,到服务器接收请求内容,触发绑定的函数并且执行相关逻辑完毕,然后返回内容给客户端的整个过程大体上要经过如下几个步骤:
全局使用: 管道 - 守卫 - 拦截器 - 过滤器 - 中间件。统一在 main.ts 文件中使用,全局生效
import { NestFactory } from '@nestjs/core'; import { ParseIntPipe } from '@nestjs/common'; import { AppModule } from './app.module'; import { HttpExceptionFilter } from './common/filters/http-exception.filter'; import { LoggerMiddleware } from './common/middleware/logger.middleware'; import { AuthGuard } from './common/guard/auth.guard'; import { AuthInterceptor } from './common/interceptors/auth.interceptor'; async function bootstrap() { const app = await NestFactory.create(AppModule); //全局使用管道:这里使用的是内置,也可以使用自定义管道,在下文 app.useGlobalPipes(new ParseIntPipe()); //全局使用中间件 app.use(LoggerMiddleware) //全局使用过滤器 //这里使用的是自定义过滤器,先别管,先学会怎么在全局使用 app.useGlobalFilters(new HttpExceptionFilter()); //全局使用守卫 app.useGlobalGuards(new AuthGuard()); //全局使用拦截器 app.useGlobalInterceptors(new AuthInterceptor()); await app.listen(3000); } bootstrap();
管道
常用内置管道,从 @nestjs/common
导出
ParseIntPipe
:将字符串数字转数字ValidationPipe
:验证管道
局部使用管道
- 匹配整个路径,使用 UsePipes
- 只匹配某个接口,使用 UsePipes
- 在获取参数时匹配,一般使用内置管道
import { Controller, Get, Put, Body, Param, UsePipes, ParseIntPipe } from '@nestjs/common'; import { myPipe } from '../../common/pipes/user.pipe'; @Controller('user') @UsePipes(new myPipe()) //局部方式 1:匹配整个/user, get 请求和 put 请求都会命中 export class UserController { @Get(':id') getUserById(@Param('id', new ParseIntPipe()) id) { //局部方式 3:只匹配/user 的 get 请求,使用的是内置管道 console.log('user', typeof id); return id; } @Put(':id') @UsePipes(new myPipe()) //局部方式 2:只匹配/user 的 put 请求 updateUser(@Body() user, @Param('id') id) { return { user, id, }; } }
自定义管道
使用快捷命令生成: nest g pi myPipe common/pipes
import { ArgumentMetadata, Injectable, PipeTransform, BadRequestException, } from '@nestjs/common'; //自定义管道必须实现自 PipeTransform,固定写法,该接口有一个 transform 方法 //transform 参数: //value:使用 myPipe 时所传递的值,可以是 param 传递的的查询路径参数,可以是 body 的请求体 //metadata:元数据,可以用它判断是来自 param 或 body 或 query @Injectable() export class myPipe implements PipeTransform<string> { transform(value: string, metadata: ArgumentMetadata) { if (metadata.type === 'body') { console.log('来自请求体', value); } if (metadata.type === 'param') { console.log('来自查询路径', value); const val = parseInt(value, 10); //如果不是传递一个数字,抛出错误 if (isNaN(val)) { throw new BadRequestException('Validation failed'); } return val; } return value; } }
守卫
自定义守卫
使用快捷命令生成: nest g gu myGuard common/guards
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; //反射器,作用与自定义装饰器桥接,获取数据 //自定义守卫必须 CanActivate,固定写法,该接口只有一个 canActivate 方法 //canActivate 参数: //context:请求的(Response/Request) 的引用 //通过守卫返回 true,否则返回 false,返回 403 状态码 @Injectable() export class AuthGuard implements CanActivate { constructor(private readonly reflector: Reflector) { } // 白名单数组 private whiteUrlList: string[] = ['/user']; // 验证该次请求是否为白名单内的路由 private isWhiteUrl(urlList: string[], url: string): boolean { if (urlList.includes(url)) { return true; } return false; } canActivate(context: ExecutionContext): boolean { // 获取请求对象 const request = context.switchToHttp().getRequest(); //console.log('request', request.headers); //console.log('request', request.params); //console.log('request', request.query); //console.log('request', request.url); // 用法一:验证是否是白名单内的路由 if (this.isWhiteUrl(this.whiteUrlList, request.url)) { return true; } else { return false; } // 用法二:使用反射器,配合装饰器使用,获取装饰器传递过来的数据 const roles = this.reflector.get<string[]>('roles', context.getHandler()); //console.log(roles); // [ 'admin' ] //http://localhost:3000/user/9?user=admin,如果与装饰器传递过来的值匹配则通过,否则不通过 //真实开发中可能从 cookie 或 token 中获取值 const { user } = request.query; if (roles.includes(user)) { return true; } else { return false; } // 其他用法 // 获取请求头中的 token 字段 const token = context.switchToRpc().getData().headers.token; // console.log('token', token); // 获取 session const userinfo = context.switchToHttp().getRequest().session; // console.log('session', userinfo); return true; } }
局部使用守卫
import { Controller, Get, Delete, Param, UsePipes, UseGuards, ParseIntPipe, } from '@nestjs/common'; import { AuthGuard } from '../../common/guard/auth.guard'; import { Role } from '../../common/decorator/role.decorator'; //自定义装饰器 @UseGuards(AuthGuard) //局部使用守卫,守卫整个 user 路径 @Controller('user') export class UserController { @Get(':id') getUserById(@Param('id', new ParseIntPipe()) id) { console.log('user', typeof id); return id; } @Delete(':id') @Role('admin') //使用自定义装饰器,传入角色,必须是 admin 才能删除 removeUser(@Param('id') id) { return id; } }
装饰器
自定义守卫中使用到了自定义装饰器
nest g d role common/decorator
//这是快捷生成的代码 import { SetMetadata } from '@nestjs/common'; //SetMetadata 作用:将获取到的值,设置到元数据中,然后守卫通过反射器才能获取到值 export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
拦截器
使用快捷命令生成: nest g in auth common/intercepters
import { CallHandler, ExecutionContext, Injectable, NestInterceptor, } from '@nestjs/common'; import { map } from 'rxjs/operators'; import { Observable } from 'rxjs'; //自定义拦截器必须实现自 NestInterceptor,固定写法,该接口只有一个 intercept 方法 //intercept 参数: //context:请求上下文,可以拿到的 Response 和 Request @Injectable() export class AuthInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const request = context.switchToHttp().getRequest(); console.log('拦截器', request.url); return next.handle().pipe( map((data) => { console.log('全局响应拦截器方法返回内容后...'); return { status: 200, timestamp: new Date().toISOString(), path: request.url, message: '请求成功', data: data, }; }), ); } }
过滤器
局部使用过滤器
import { Controller, Get, UseFilters, HttpException, HttpStatus, } from '@nestjs/common'; import { HttpExceptionFilter } from '../../common/filters/http-exception.filter'; //局部使用过滤器 @UseFilters(new HttpExceptionFilter()) @Controller('/user') export class ExceptionController { @Get() getUserById(@Query() { id }): string { if (!id) { throw new HttpException( { status: HttpStatus.BAD_REQUEST, message: '请求参数 id 必传', error: 'id is required', }, HttpStatus.BAD_REQUEST, ); } return 'hello error'; } }
自定义过滤器
使用快捷命令生成: nest g f myFilter common/filters
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, } from '@nestjs/common'; //必须实现至 ExceptionFilter,固定写法,该接口只有一个 catch 方法 //catch 方法参数: //exception:当前正在处理的异常对象 //host:传递给原始处理程序的参数的一个包装(Response/Request) 的引用 @Catch(HttpException) export class HttpExceptionFilter implements ExceptionFilter<HttpException> { catch(exception: HttpException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception.getStatus(); //获取状态码 const exceptionRes: any = exception.getResponse(); //获取响应对象 const { error, message } = exceptionRes; //自定义的异常响应内容 const msgLog = { status, timestamp: new Date().toISOString(), path: request.url, error, message, }; response.status(status).json(msgLog); } }
中间件
局部使用中间件
import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common'; import { LoggerMiddleware } from './common/middleware/logger.middlerware'; import { UserModule } from './modules/user/user.module'; @Module({ imports:[ UserModule ] }) export class AppModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) //应用中间件 .exclude({ path: 'user', method: RequestMethod.POST }) //排除 user 的 post 方法 .forRoutes('user'); //监听路径 参数:路径名或*,*是匹配所以的路由 // .forRoutes({ path: 'user', method: RequestMethod.POST }, { path: 'album', method: RequestMethod.ALL }); //多个 // .apply(UserMiddleware) //支持多个中间件 // .forRoutes('user') } }
自定义中间件
nest g mi logger common/middleware
import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response } from 'express'; @Injectable() export class LoggerMiddleware implements NestMiddleware { //req:请求参数 //res:响应参数 //next:执行下一个中件间 use(req: Request, res: Response, next: () => void) { const { method, path } = req; console.log(`${method} ${path}`); next(); } }
函数式中间件
// 函数式中间件-应用于全局 export function logger(req, res, next) { next(); } // main.ts async function bootstrap() { // 创建实例 const app = await NestFactory.create<NestExpressApplication>(AppModule); // 设置全局日志函数中间件 app.use(logger); } bootstrap();
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论