手写express框架的中间件

  对于使用过nodejs中express框架的同学应该很熟悉中间件是express的核心部分,接下来我们将中间件的实现尝试写一下.

  在开发之前我先整理一下express的API,再通过这些API去反推它的实现:

//express的实例有哪些用法再反推它怎么实现.
//下面是伪代码部分

const app = new express();

app.use((req,res,next)=>{
     next();
})

app.use("/api",(req,res,next)=>{
    next();
})

app.post("/login",(req,res)=>{
   res.json({result:1,msg:"我是post请求"});
})

function middleWare(req,res,next){
  console.log("我是中间件");
  next(); 
}

app.get("/getContent",middleWare,()=>{
  res.json({result:1,msg:"我是get请求"});
})

app.listen(8080)

 从上面的API我们可以看出需要实现几个方法,分别是:json,get,post,listen和use.其中我们格外要注意的是中间件前面还可以写多个中间件.

接下来就是核心的实现文件index.js

const http = require("http");

class express {

  app = null;

  funList = {
    middleWare: [], //存储中间件的方法
    get: {}, //存储get请求的方法
    post: {}, //存储post请求的方法
    all: {}
  }

  constructor() {

    this.app = http.createServer((req, res) => {

      res.json = (data) => {

        res.setHeader('Content-Type', 'application/json');

        res.end(JSON.stringify(data));

      }

      if (req.url !== "/favicon.ico") {
        this.requestHandler(req, res);
      }

    })

  }

  /**
   * 核心方法
   * 
   * 因为这个use方法可以定义多个,我觉得这个use方法是再收集回调函数
   * 
   */
  use(...args) {

    this.parameterVerify(args);
    this.collectRequest(args);

  }

  /**
   * 参数验证
   */
  parameterVerify(args) {

    if (args.length == 0) {
      throw new Error("中间件参数不能为空!");
    } else if (args.length == 1 && typeof args[0] !== "function") {
      throw new Error("中间件函数不能为空!");
    }

    const middleWare = args[0];

    if (typeof middleWare === "function") {
      args.unshift("*");
    }

  }

  collectRequest(args, method = null) {

    const path = args[0];

    const fun_arr = args.slice(1);

    let arr = [];

    fun_arr.forEach((fun) => {

      arr.push({
        path,
        callback: fun
      });

    })

    if (path === "*") {//这是个中间件

      this.funList.middleWare = this.funList.middleWare.concat(arr);

    } else if (method === null) { //任何请求都满足

      this.funList.all[path] = arr;

    } else if (method) { //为某种特定的方法

      const method_lower = method.toLowerCase();

      this.funList[method_lower][path] = arr;

    }

  }


  /**
   * 
   * @param {*} req 
   * @param {*} res 
   * 获取请求要运行的函数栈
   */
  getStack(req) {

    const { url, method } = req;

    let arr = [...this.funList.middleWare];

    try {
      const data = this.funList[method.toLowerCase()][url];
      arr = arr.concat((Array.isArray(data) ? data : []));
    } catch (error) {

    }

    arr = arr.concat(this.funList.all[url] ? this.funList.all[url] : []);

    return arr;

  }


  /**
   * 处理请求
   */
  requestHandler(req, res) {

    let stacks = this.getStack(req);

    const next = () => {

      const fun = stacks.shift();

      if (fun) {
        fun.callback(req, res, next);
      }

    }

    next();

  }

  /**
   * 
   * @param {*} port 
   */
  get(...args) {

    this.parameterVerify(args);

    this.collectRequest(args, "GET");

  }

  /**
  * 
  * @param {*} port 
  */
  post(...args) {

    this.parameterVerify(args);

    this.collectRequest(args, "POST");

  }


  /**
   * 创建服务器的方法
   */
  listen(port) {
    this.app.listen(port);
  }

}

module.exports = express;

分析:我们主要利用原生模块http的createServer方法来实现.在这里将定义的中间件分为四类:普通中间件函数,get请求方法,post请求方法和任意请求方法.将定义的中间件全部分类好装入这四类数据中.当用户的请求进来,通过req的url和method找到该请求需要运行的中间件函数链(在上面的四类数据中寻找),随后用嵌套的方式运行函数链,从而就实现中间件函数的依此执行.上面代码最核心的部分就是next方法的实现,通过shift移除数组的第一个元素不断嵌套运行next方法达到依此执行函数链的效果.

 

最后我们也可以写一个测试文件来测试上面代码test.js

const express = require("./index");

const app = new express();

app.use((req, res, next) => {

  console.log(123);

  next();

})

app.use((req, res, next) => {

  console.log(456);

  next();

})

function middleWare(req, res, next) {

  setTimeout(() => {
    console.log('789')
    next()
  }, 2000)

}

app.use(middleWare, middleWare, (req, res, next) => {

  console.log('101112')

  next();

})

app.use("/api", (req, res) => {

  res.end("your request url is api");

})


app.get("/request", (req, res) => {

  res.end("i'm is get");

})

app.post("/request", (req, res) => {

  res.end("i'm is post");

})

app.use("/", (req, res) => {
  res.json({ msg: "not found!" });
})

app.listen(8080);

  最后:将test.js和index.js放在同一目录,在当前目录下打开命令行工具,输入node ./test.js启动node服务(当然前提是要安装好node环境),在浏览器输入http://localhost:8080/查看运行结果.

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值