浅谈express 中间件机制及实现原理


Posted in Javascript onAugust 31, 2017

简介

中间件机制可以让我们在一个给定的流程中添加一个处理步骤,从而对这个流程的输入或者输出产生影响,或者产生一些中作用、状态,或者拦截这个流程。中间件机制和tomcat的过滤器类似,这两者都属于责任链模式的具体实现。

express 中间件使用案例

let express = require('express')
let app = express()
//解析request 的body
app.use(bodyParser.json())
//解析 cookie
app.use(cookieParser())
//拦截
app.get('/hello', function (req, res) {
 res.send('Hello World!');
});

模拟中间件机制并且模拟实现解析request的中间件

首先模拟一个request

request = { //模拟的request
  requestLine: 'POST /iven_ HTTP/1.1',
  headers: 'Host:www.baidu.com\r\nCookie:BAIDUID=E063E9B2690116090FE24E01ACDDF4AD:FG=1;BD_HOME=0',
  requestBody: 'key1=value1&key2=value2&key3=value3',
}

一个http请求分为请求行、请求头、和请求体,这三者之间通过\r\n\r\n即一个空行来分割,这里假设已经将这三者分开,requestLine(请求行)中有方法类型,请求url,http版本号,这三者通过空格来区分,headers(请求头)中的各部分通过\r\n来分割,requestBody(请求体)中通过 & 来区分参数

模拟中间件机制

约定 中间件一定是一个函数并且接受 request, response, next三个参数

function App() {
  if (!(this instanceof App))
    return new App();
  this.init();
}
App.prototype = {
  constructor: App,
  init: function() {
    this.request = { //模拟的request
      requestLine: 'POST /iven_ HTTP/1.1',
      headers: 'Host:www.baidu.com\r\nCookie:BAIDUID=E063E9B2690116090FE24E01ACDDF4AD:FG=1;BD_HOME=0',
      requestBody: 'key1=value1&key2=value2&key3=value3',
    };
    this.response = {}; //模拟的response
    this.chain = []; //存放中间件的一个数组
    this.index = 0; //当前执行的中间件在chain中的位置
  },
  use: function(handle) { //这里默认 handle 是函数,并且这里不做判断
    this.chain.push(handle);
  },
  next: function() { //当调用next时执行index所指向的中间件
    if (this.index >= this.chain.length)
      return;
    let middleware = this.chain[this.index];
    this.index++;
    middleware(this.request, this.response, this.next.bind(this));
  },
}

对 request 处理的中间件

function lineParser(req, res, next) {
    let items = req.requestLine.split(' ');
    req.methond = items[0];
    req.url = items[1];
    req.version = items[2];
    next(); //执行下一个中间件
  }

function headersParser(req, res, next) {
  let items = req.headers.split('\r\n');
  let header = {}
  for(let i in items) {
    let item = items[i].split(':');
    let key = item[0];
    let value = item[1];
    header[key] = value;
  }
  req.header = header;
  next(); //执行下一个中间件
}

function bodyParser(req, res, next) {
  let bodyStr = req.requestBody;
  let body = {};
  let items = bodyStr.split('&');
  for(let i in items) {
    let item = items[i].split('=');
    let key = item[0];
    let value = item[1];
    body[key] = value;
  }
  req.body = body;
  next(); //执行下一个中间件
}

function middleware3(req, res, next) {
  console.log('url: '+req.url);
  console.log('methond: '+req.methond);
  console.log('version: '+req.version);
  console.log(req.body);
  console.log(req.header);
  next(); //执行下一个中间件
}

测试代码

let app = App();
app.use(lineParser);
app.use(headersParser);
app.use(bodyParser);
app.use(middleware3);
app.next();

整体代码

function App() {
  if (!(this instanceof App))
    return new App();
  this.init();
}
App.prototype = {
  constructor: App,
  init: function() {
    this.request = { //模拟的request
      requestLine: 'POST /iven_ HTTP/1.1',
      headers: 'Host:www.baidu.com\r\nCookie:BAIDUID=E063E9B2690116090FE24E01ACDDF4AD:FG=1;BD_HOME=0',
      requestBody: 'key1=value1&key2=value2&key3=value3',
    };
    this.response = {}; //模拟的response
    this.chain = []; //存放中间件的一个数组
    this.index = 0; //当前执行的中间件在chain中的位置
  },
  use: function(handle) { //这里默认 handle 是函数,并且这里不做判断
    this.chain.push(handle);
  },
  next: function() { //当调用next时执行index所指向的中间件
    if (this.index >= this.chain.length)
      return;
    let middleware = this.chain[this.index];
    this.index++;
    middleware(this.request, this.response, this.next.bind(this));
  },
}
function lineParser(req, res, next) {
    let items = req.requestLine.split(' ');
    req.methond = items[0];
    req.url = items[1];
    req.version = items[2];
    next(); //执行下一个中间件
  }

function headersParser(req, res, next) {
  let items = req.headers.split('\r\n');
  let header = {}
  for(let i in items) {
    let item = items[i].split(':');
    let key = item[0];
    let value = item[1];
    header[key] = value;
  }
  req.header = header;
  next(); //执行下一个中间件
}

function bodyParser(req, res, next) {
  let bodyStr = req.requestBody;
  let body = {};
  let items = bodyStr.split('&');
  for(let i in items) {
    let item = items[i].split('=');
    let key = item[0];
    let value = item[1];
    body[key] = value;
  }
  req.body = body;
  next(); //执行下一个中间件
}

function middleware3(req, res, next) {
  console.log('url: '+req.url);
  console.log('methond: '+req.methond);
  console.log('version: '+req.version);
  console.log(req.body);
  console.log(req.header);
  next(); //执行下一个中间件
}
let app = App();
app.use(lineParser);
app.use(headersParser);
app.use(bodyParser);
app.use(middleware3);
app.next();

运行结果

将以上整体代码运行后将打印以下信息

url: /iven_
methond: POST
version: HTTP/1.1
{key1: "value1", key2: "value2", key3: "value3"}
{Host: "www.baidu.com", Cookie: "BAIDUID=E063E9B2690116090FE24E01ACDDF4AD"}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript图像处理—平滑处理实现原理
Dec 28 Javascript
IE8的JavaScript点击事件(onclick)不兼容的解决方法
Nov 22 Javascript
javascript实现图片循环渐显播放的方法
Feb 24 Javascript
配置Grunt的Task时通配符支持和动态生成文件名问题
Sep 06 Javascript
原生JS封装Ajax插件(同域、jsonp跨域)
May 03 Javascript
Javascript Event(事件)的传播与冒泡
Jan 23 Javascript
限时抢购-倒计时的完整实例(分享)
Sep 17 Javascript
echarts同一页面中四个图表切换的js数据交互方法示例
Jul 03 Javascript
原生JS实现自定义下拉单选选择框功能
Oct 12 Javascript
详解vue 项目白屏解决方案
Oct 31 Javascript
redux.js详解及基本使用
May 24 Javascript
vue项目中常见问题及解决方案(推荐)
Oct 21 Javascript
JavaScript 通过Ajax 动态加载CheckBox复选框
Aug 31 #Javascript
BootStrap下的弹出框加载select2框架失败的解决方法
Aug 31 #Javascript
Angular2 http jsonp的实例详解
Aug 31 #Javascript
BootStrap中Table隐藏后显示问题的实现代码
Aug 31 #Javascript
Cpage.js给组件绑定事件的实现代码
Aug 31 #Javascript
基于canvas粒子系统的构建详解
Aug 31 #Javascript
Vue 组件间的样式冲突污染
Aug 31 #Javascript
You might like
PHP实现多服务器session共享之NFS共享的方法
2007/03/16 PHP
关于php fread()使用技巧
2010/01/22 PHP
PHP中大于2038年时间戳的问题处理方案
2015/03/03 PHP
php使用CURL不依赖COOKIEJAR获取COOKIE的方法
2015/06/17 PHP
php实现Session存储到Redis
2015/11/11 PHP
php实现三级级联下拉框
2016/04/17 PHP
thinkphp3.x中display方法及show方法的用法实例
2016/05/19 PHP
thinkPHP5.0框架独立配置与动态配置方法
2017/03/17 PHP
使用Javascript和DOM Interfaces来处理HTML
2006/10/09 Javascript
JS控制日期显示的小例子
2013/11/23 Javascript
元素绑定click点击事件方法
2015/06/08 Javascript
JavaScript前端开发之实现二进制读写操作
2015/11/04 Javascript
Angular中$cacheFactory的作用和用法实例详解
2016/08/19 Javascript
javascript实现获取图片大小及图片等比缩放的方法
2016/11/24 Javascript
vue组件Prop传递数据的实现示例
2017/08/17 Javascript
javascript自定义事件功能与用法实例分析
2017/11/08 Javascript
解决Angular.js中使用Swiper插件不能滑动的问题
2018/02/26 Javascript
关于js对textarea换行符的处理方法浅析
2018/08/03 Javascript
详解vuex持久化插件解决浏览器刷新数据消失问题
2019/04/15 Javascript
如何在Express4.x中愉快地使用async的方法
2020/11/18 Javascript
深入理解Python 代码优化详解
2014/10/27 Python
Python CSV模块使用实例
2015/04/09 Python
python3新特性函数注释Function Annotations用法分析
2016/07/28 Python
python操作oracle的完整教程分享
2018/01/30 Python
python连接mongodb密码认证实例
2018/10/16 Python
简单分析python的类变量、实例变量
2019/08/23 Python
520使用Python实现“我爱你”表白
2020/05/20 Python
基于keras中的回调函数用法说明
2020/06/17 Python
python日志通过不同的等级打印不同的颜色(示例代码)
2021/01/13 Python
Html5页面上如何禁止手机虚拟键盘弹出
2020/03/19 HTML / CSS
阿尔卡特(中国)的面试题目
2014/08/20 面试题
售后主管岗位职责
2013/12/08 职场文书
幼儿园亲子活动总结
2014/04/26 职场文书
社区戒毒工作方案
2014/06/04 职场文书
领导干部整治奢华浪费之风思想汇报
2014/10/07 职场文书
如何用python识别滑块验证码中的缺口
2021/04/01 Python