认识中间件-以及两个简单的示例

认识中间件-以及两个简单的示例

什么是中间件

官方文档

中间件是在路由处理程序之前调用的函数。中间件函数可以访问请求和响应对象,以及next()应用程序请求-响应周期中的中间件函数。下一个中间件函数通常用名为 的变量表示next。类似于express的中间件。

官方文档也说了。

多说无益,我们来讲如何写中间件以及如何使用中间件。

一个响应处理中间件

这是我们之前的一个Post请求

可以看到,响应直接就是name了,正常情况下,我们需要包装一下,比如套个data加个code和message等等。当然,我们可以新建一个响应体类来处理好比如下代码,这里我们不用,我们用中间件来实现。

ts 复制代码
export default class ResObj {
  code: number;
  data: any;
  msg: string;
  constructor(code: number, data: any, msg: string) {
    this.code = code;
    this.data = data;
    this.msg = msg;
  }
}

老朋友 nest g

我们可以使用nest g 来帮我们生成一个中间件。

首先进入到apps/demo/src目录下

sh 复制代码
nest g mi response 

指令帮我们创建的也许没有类型定义,去官方爬一份。

ts 复制代码
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class ResponseMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    next();
  }
}

如何使用

我们新建了一个中间件,但是没有写逻辑,我们先来引入下,这样可以直观的看数据。

为某个module引入

中间件可以单为某一个模块使用。

这里以demo2模块为例子

ts 复制代码
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { Demo2Service } from './demo2.service';
import { Demo2Controller } from './demo2.controller';
import { ResponseMiddleware } from '../response/response.middleware';

@Module({
  controllers: [Demo2Controller],
  providers: [Demo2Service],
})
export class Demo2Module implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(ResponseMiddleware).forRoutes(Demo2Controller);
  }
}

主要代码在这里,为了有代码提示,我们implements NestModule

如果你的编辑器有代码提示,这些输入几个字母就出现了。这里就为某个模块引入成功了。如果有多个中间件怎么办。

ts 复制代码
consumer.apply(middle1, middl12,
middle3()// 如果你是函数式写法
).forRoutes(CatsController);
// 可以使用 forRoutes("*") 对模块下所有控制器

这里的forRoutes可以不为整个controller。可以排除一些,也可以只写入固定。

其他详情,看文档

全局引入

use()

编写逻辑

我们这里把next()注释掉,不执行,会发生什么呢

请求会被挂起。

去掉注释,将全局引入的注释掉,只用模块内引入,我们来编写逻辑,实现响应的格式化。

这里做了简单的处理,没有处理异常。后面我们会用其他基础构件来实现响应的处理。

ts 复制代码
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

function isJson(str: string) {
  try {
    JSON.parse(str);
  } catch (e) {
    return false;
  }
  return true;
}

@Injectable()
export class ResponseMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const originalSend = res.send;
    res.send = function (body) {
      const newBody = {
        code: 200,
        message: 'Success',
        data: isJson(body) ? JSON.parse(body) : body,
      };
      return originalSend.call(this, JSON.stringify(newBody));
    };
    next();
  }
}

此时我们再次请求

当然我们除了处理响应,还可以处理请求。

比如我们这里加个。

这样是不推荐的,除非你有对应的取参数处理方法。不然你这里读取的就有问题,类型也不对了。


一个日志中间件

nest g mi 生成

我们同样在apps/demo/src下生成

sh 复制代码
nest g mi logger

引入

ts 复制代码
export class Demo2Module implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(ResponseMiddleware, LoggerMiddleware)
      .forRoutes(Demo2Controller);
  }
}

逻辑类似于上面的响应处理,我们获取了请求的参数,这里Log了部分,其实可以将打印的东西打印到文件,接入fs或者用logger相关的库,来帮我们实现。这里只做中间件的简单演示,后面在实战中会进一步的熟悉。

ts 复制代码
import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const time = new Date().getTime();
    Logger.log(`${time}:[${req.method}] -req- ${req.url} -body: ${JSON.stringify(req.body)}`);
    const originalSend = res.send;
    res.send = function (body) {
      Logger.log(`${time}:[${req.method}] -res- ${req.url} -data: ${JSON.stringify(body)}`);
      return originalSend.call(this, body);
    };
    next();
  }
}

思考

可以看到我们这里打印的不是上个中间件的处理结果,上个中间件明明我们逃了一层code和data。为什么呢?

那如果我们换下中间件的引入顺序呢。改成如下

可以看到这次打印的是之前的响应中间件处理后的数据。

当请求抵达应用时,它会按照中间件在 apply 方法里的顺序依次通过各个中间件。每个中间件在处理完请求后,若调用了 next() 函数,就会把控制权传递给下一个中间件或者控制器;若没有调用 next() 函数,请求处理流程就会在此中断。

当控制器处理完请求并返回响应时,响应会反向通过中间件,即最后一个中间件先处理响应,接着是倒数第二个,依此类推。

代码进度

仓库

相关推荐
用户67570498850244 分钟前
从失真到绝对精准:Go语言中如何进行高精度计算?
后端·go
程序员cxuan1 小时前
GPT-5.6 官宣解禁,明天全量发布!
人工智能·后端·程序员
郑州光合科技余经理1 小时前
海外外卖平台源码改造实战——多语言核心代码实现
java·开发语言·前端·后端·mysql·架构·php
喜欢打篮球的普通人2 小时前
Trition程序编写:从“Hello CUDA“到“Hello Triton“:向量加法背后的编译黑魔法
开发语言·后端·rust
苏三说技术2 小时前
Spring AI 2.0和Spring AI Alibaba,哪个更好?
后端
用户6757049885022 小时前
还在用字符串返回错误提示?Go语言优雅错误处理的正确姿势!
后端·go
雪隐2 小时前
用Flutter做背单词APP-02我画了设计稿,然后让AI帮我设计了数据库(顺便聊了会天)
前端·人工智能·后端
用户938515635073 小时前
从零打造你的第一个智能体(Agent):*手写一个能自主建项目的Mini Cursor (三)
javascript·人工智能·后端
Alan_753 小时前
GraphQL 深度解析:为什么它能替代传统 REST API
数据库·后端
SamDeepThinking3 小时前
为什么很多人学了Java,还是没真正理解类和对象?
java·后端·面试