中间件
中间件是在路由处理程序之前调用的函数。中间件函数可以访问请求和响应对象
中间件函数可以执行以下任务:
- 执行任何代码。
- 对请求和响应对象进行更改。
- 结束请求-响应周期。
- 调用堆栈中的下一个中间件函数。
- 如果当前的中间件函数没有结束请求-响应周期, 它必须调用 next() 将控制传递给下一个中间件函数。否则, 请求将被挂起。
1.创建一个依赖注入中间件
要求我们实现use函数,返回req,res,next参数,如果不调用next 程序将被挂起
先新建一个中间件logger
nest g mi logger
use方法源码,可以看到req是客户端传递给服务端的数据对象,里面包含了一些请求参数等,res是服务端返回给客户端的数据对象。
然后在user.module中实现NestModule,实现接口里面的configure方法,返回一个消费者 ,consumer通过apply注册中间件,通过forRoutes指定Controller路由
第一种在forRoutes使用字符串
import { Global, MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { LoggerMiddleware as Logger } from '../logger/logger.middleware'
@Global()
@Module({
controllers: [UserController],
providers: [UserService],
exports: [UserService]
})
export class UserModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(Logger).forRoutes('user')
}
}
中间件Logger
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log("zl");
next();
}
}
访问http://localhost:3000/user,控制台输出zl
如果注释掉next(),访问http://localhost:3000/user,此时服务被挂起,页面一直加载
也可以服务端直接返回数据给客户端,告诉客户端“我被拦截了”
注意,res.send和next只能存在一个
第二种在forRoutes使用对象配置
也可以指定拦截的方法,比如拦截GET、 POST等,在forRoutes使用对象配置
import { Global, MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { LoggerMiddleware as Logger } from '../logger/logger.middleware'
@Global()
@Module({
controllers: [UserController],
providers: [UserService],
exports: [UserService]
})
export class UserModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(Logger).forRoutes({ path: "user", method: RequestMethod.GET })
}
}
此时访问http://localhost:3000/user,调用的是controller里面的findAll方法,它为GET方法,所以被拦截了
如果换成拦截POST方法,则页面正常输出了
consumer.apply(Logger).forRoutes({ path: "user", method: RequestMethod.POST })
第三种在forRoutes直接把Controller塞进去
export class UserModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(Logger).forRoutes(UserController)
}
}
2.全局中间件
注意全局中间件只能使用函数模式。
案例:可以做白名单拦截之类的,比如只让'/list'接口访问,其余都拦截
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { VersioningType } from '@nestjs/common';
const whiteList = ['/list']
function middleWareAll(req: any, res: any, next: any) {
console.log(req.originalUrl);
if (whiteList.includes(req.originalUrl)) {
next()
} else {
res.send('我无法访问了')
}
}
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableVersioning({
type: VersioningType.URI
})
app.use(middleWareAll)
await app.listen(3000);
}
bootstrap();
比如访问http://localhost:3000/list,正常放行
访问http://localhost:3000/user,被拦截
3.接入第三方中间件
例如cors处理跨域
npm install cors
npm install @types/cors -D
在百度页面,控制台利用fetch访问http://localhost:3000/user会出现跨域问题
fetch('http://localhost:3000/user').then((res)=>res.json()).then((res)=>{
console.log(res)
})
解决办法:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { VersioningType } from '@nestjs/common';
import * as cors from 'cors';
const whiteList = ['/list']
function middleWareAll(req: any, res: any, next: any) {
console.log(req.originalUrl);
if (whiteList.includes(req.originalUrl)) {
next()
} else {
res.send({
message: 'wozuishuai'
})
}
}
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableVersioning({
type: VersioningType.URI
})
app.use(cors())
app.use(middleWareAll)
await app.listen(3000);
}
bootstrap();
注意: app.use(cors())和app.use(middleWareAll)的调用顺序不要弄反了,先是解决跨域再使用中间件,顺序出错跨域还是会出现。
再次访问就不会有跨域了