JavaScript 实现类似Express的中间件系统(实例详解)

yizhihongxing

来详细讲解一下“JavaScript 实现类似Express的中间件系统(实例详解)”的攻略。

简介

中间件是实现 Express 等框架功能的核心。本文主要讲解如何通过 JavaScript 实现一个类似 Express 的中间件系统。

实现过程

1. 实现基本的 Application

首先,我们需要创建一个 Application 类,表示整个应用。这个类需要实现以下功能:

  • 注册路由和中间件
  • 解析请求体和响应头
  • 处理错误
class Application {
  constructor() {
    // 路由和中间件列表
    this.routes = {
      all: [],
      get: [],
      post: []
    };
  }

  // 注册路由和中间件
  use(path, middleware) {
    let info = {};
    if (typeof path === "string") {
      info.path = path;
      info.middleware = middleware;
    } else {
      info.path = "/";
      info.middleware = path;
    }
    this.routes.all.push(info);
  }

  // 处理请求
  handle(req, res) {
    let self = this;
    let idx = 0;
    let stack = self.routes.all;

    function next() {
      let middleware = stack[idx];
      if (!middleware) return;
      if (middleware.path === "/" || middleware.path === req.path || middleware.path === req.url) {
        middleware.middleware(req, res, next);
      } else {
        next();
      }
    }

    next();
  }

  // 监听端口
  listen(...args) {
    let server = http.createServer(this.handle.bind(this));
    server.listen(...args);
  }
}

2. 实现中间件

中间件的实现方式很简单,它们是一个函数,接收 reqresnext 三个参数,最后必须调用 next,否则后续的中间件将无法执行。

3. 注册路由和中间件

我们可以通过 use 方法注册路由和中间件,这个方法接收两个参数,第一个参数表示路径,可以是字符串或正则表达式;第二个参数表示中间件。

4. 处理请求

在处理请求时,我们需要遍历所有注册的中间件和路由,直到找到匹配的路由或中间件,然后依次执行它们。

class Application {
  // 省略其他代码

  handle(req, res) {
    let self = this;
    let idx = 0;
    // 获取指定方法类型的路由和中间件
    let method = req.method.toLowerCase();
    let stack = self.routes.all.concat(self.routes[method]);

    function next() {
      let middleware = stack[idx];
      if (!middleware) return;
      // 匹配路径
      if (middleware.path === "/" || middleware.path === req.path || middleware.path === req.url) {
        middleware.middleware(req, res, next);
      } else {
        idx++;
        next();
      }
    }

    next();
  }
}

5. 解析请求体和响应头

为了方便使用,在解析请求体和响应头时,我们可以定义一些常见的中间件,如下:

// 解析 JSON 格式请求体
const bodyParserJSON = function (req, res, next) {
  let data = "";
  req.on("data", function (chunk) {
    data += chunk;
  });
  req.on("end", function () {
    try {
      req.body = JSON.parse(data);
      next();
    } catch (err) {
      res.statusCode = 400;
      return res.end("bad request.");
    }
  });
};

// 解析表单请求体
const bodyParserUrlencoded = function (req, res, next) {
  let data = "";
  req.on("data", function (chunk) {
    data += chunk;
  });
  req.on("end", function () {
    req.body = qs.parse(data);
    next();
  });
};

// 设置响应头
const setContentType = function (req, res, next) {
  res.setHeader("Content-Type", "text/html;charset=utf-8");
  next();
};

6. 错误处理

最后,在处理请求过程中,难免会出现一些错误。我们可以通过全局中间件来捕获错误并处理,如下:

// 全局错误处理中间件
const errorHandler = function (err, req, res, next) {
  console.error(err);
  res.statusCode = 500;
  res.end("Internal Server Error");
};

app.use(setContentType);
app.use(bodyParserJSON);
app.use(bodyParserUrlencoded);
app.use(errorHandler);

示例说明

接下来,我们使用上述代码创建一个简单的 HTTP 服务器,并实现以下两个接口:

  • GET /,返回 “Hello World!”
  • POST /api/user,返回请求体中的 name 字段
const http = require("http");
const { parse } = require("url");
const qs = require("qs");

class Application {
  constructor() {
    // 路由和中间件列表
    this.routes = {
      all: [],
      get: [],
      post: []
    };
  }

  // 注册路由和中间件
  use(path, middleware) {
    let info = {};
    if (typeof path === "string") {
      info.path = path;
      info.middleware = middleware;
    } else {
      info.path = "/";
      info.middleware = path;
    }
    this.routes.all.push(info);
  }

  // 处理请求
  handle(req, res) {
    let self = this;
    let idx = 0;
    // 获取指定方法类型的路由和中间件
    let method = req.method.toLowerCase();
    let stack = self.routes.all.concat(self.routes[method]);

    function next() {
      let middleware = stack[idx];
      if (!middleware) return;
      // 匹配路径
      if (middleware.path === "/" || middleware.path === req.path || middleware.path === req.url) {
        middleware.middleware(req, res, next);
      } else {
        idx++;
        next();
      }
    }

    next();
  }

  // 监听端口
  listen(...args) {
    let server = http.createServer(this.handle.bind(this));
    server.listen(...args);
  }
}

// 解析 JSON 格式请求体
const bodyParserJSON = function (req, res, next) {
  let data = "";
  req.on("data", function (chunk) {
    data += chunk;
  });
  req.on("end", function () {
    try {
      req.body = JSON.parse(data);
      next();
    } catch (err) {
      res.statusCode = 400;
      return res.end("bad request.");
    }
  });
};

// 解析表单请求体
const bodyParserUrlencoded = function (req, res, next) {
  let data = "";
  req.on("data", function (chunk) {
    data += chunk;
  });
  req.on("end", function () {
    req.body = qs.parse(data);
    next();
  });
};

// 设置响应头
const setContentType = function (req, res, next) {
  res.setHeader("Content-Type", "text/html;charset=utf-8");
  next();
};

// 全局错误处理中间件
const errorHandler = function (err, req, res, next) {
  console.error(err);
  res.statusCode = 500;
  res.end("Internal Server Error");
};

let app = new Application();

// GET /
app.use("/", function (req, res) {
  res.end("Hello World!");
});

// POST /api/user
app.use("/api/user", bodyParserJSON, function (req, res) {
  res.end(`Hello ${req.body.name}!`);
});

app.use(setContentType);
app.use(bodyParserUrlencoded);
app.use(errorHandler);

app.listen(3000, function () {
  console.log("Server started, listening on port 3000.");
});

运行上述代码后,在浏览器中访问 http://localhost:3000 将会返回 Hello World!,在 Postman 等工具中发送 POST 请求,请求体包含一个 name 字段,则会返回 Hello xxx!

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript 实现类似Express的中间件系统(实例详解) - Python技术站

(0)
上一篇 2023年5月28日
下一篇 2023年5月28日

相关文章

  • JavaScript实现手写promise的示例代码

    下面是详细讲解“JavaScript实现手写promise的示例代码”的完整攻略。 理解promise 在讲解如何手写promise之前,我们先来理解一下promise,它是一个用来处理异步操作的对象。在promise中,一个异步操作被包装成一个状态机对象,该状态机由三个状态组成——pending(等待中)、fulfilled(完成)、rejected(拒绝…

    JavaScript 2023年5月28日
    00
  • Javascript 遍历对象中的子对象

    Javascript 遍历对象中的子对象通常使用递归的方式实现,具体步骤如下: 1. 判断对象是否为字典 使用 typeof 运算符判断对象类型是否为 object,进一步判断该对象是否为字典(即 {} 类型),如果不是,则直接输出当前对象: function traverseObject(obj, indentation) { if (typeof obj…

    JavaScript 2023年5月27日
    00
  • React Router 中实现嵌套路由和动态路由的示例

    针对你提出的问题,“React Router 中实现嵌套路由和动态路由的示例”的完整攻略,我将分为以下步骤进行讲解。 安装 React Router 在开始之前,首先需要安装 React Router,可以使用以下命令进行安装: npm install react-router-dom 创建基本路由 首先,我们需要创建一个基本的路由,并在其中放置一个静态页面…

    JavaScript 2023年6月11日
    00
  • javascript数组拍平方法总结

    JavaScript 数组拍平方法总结 什么是数组拍平 在 JavaScript 中可以创建多重嵌套的数组,例如: const nestedArr = [1, 2, [3, 4, [5, 6]]]; 上述数组中包含了三个元素,其中第三个元素是一个嵌套的子数组,该子数组又包含了两个元素和一个嵌套的孙子数组。这样多重嵌套的数组在实际开发中很常见。 数组拍平指的是…

    JavaScript 2023年5月27日
    00
  • jQuery实现选中弹出窗口选择框内容后赋值给文本框的方法

    要实现将弹出窗口中选中的内容赋值给文本框,可以通过以下步骤实现: 给选择框添加点击事件,使用jQuery选择器选中选择框,并使用click()事件绑定函数。 $(‘#selectBox’).click(function(){ // 在函数内部编写后续代码 }) 在函数中,打开弹出窗口,监听选择框内容的点击事件,使用jQuery选择器选中选择框内的所有选项,并…

    JavaScript 2023年6月11日
    00
  • javascript设计模式之Adapter模式【适配器模式】实现方法示例

    下面我会详细讲解“Javascript设计模式之Adapter模式【适配器模式】实现方法示例”的完整攻略,包括如何使用适配器模式以及示例的具体实现。 什么是适配器模式? 适配器模式是一种行为型设计模式,用于将一个类的接口转换成另一个客户端所期望的接口。通俗来讲,就是使得一个类能够应对多种不同的接口。 适配器模式的应用场景 在实际的编程中,适配器模式的应用场景…

    JavaScript 2023年6月10日
    00
  • JS基于贪心算法解决背包问题示例

    JS基于贪心算法解决背包问题示例 什么是贪心算法 贪心算法是一种直接寻求局部最优解以达到全局最优的算法,即采取贪心策略,每次做出当时看来最好的选择,不考虑将来的结果,也不进行回溯,只关心眼前的选择会不会对当前局面产生最优的影响。贪心算法的特点是简单、高效、易于证明正确性,并且常用于求解组合优化问题,如背包问题、最小生成树问题、哈夫曼编码等。 背包问题 背包问…

    JavaScript 2023年5月28日
    00
  • js核心基础之构造函数constructor用法实例分析

    首先,构造函数(Constructor)是JavaScript中的一个特殊函数,可以用来创建可重复使用的对象。构造函数可以用于创建特定类型的对象,比如创建一个人(Person)类型的对象。接下来我会详细讲解构造函数constructor用法实例分析。 构造函数的定义和基本使用方法 构造函数是一个用于创建对象的特殊函数,它可以使用 new 关键字来创建对象,同…

    JavaScript 2023年5月28日
    00
合作推广
合作推广
分享本页
返回顶部