浅谈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 设计模式 富有表现力的Javascript(一)
May 26 Javascript
基于Jquery的将DropDownlist的选中值赋给label的实现代码
May 06 Javascript
Jquery Change与bind事件代码
Sep 29 Javascript
hover的用法及live的用法介绍(鼠标悬停效果)
Mar 29 Javascript
jQuery动态产生select option下拉列表
Mar 15 Javascript
jQuery插件FusionCharts绘制的3D双柱状图效果示例【附demo源码】
Apr 20 jQuery
BootStrap中的Fontawesome 图标
May 25 Javascript
vue自定义移动端touch事件之点击、滑动、长按事件
Jul 10 Javascript
详解vue服务端渲染浏览器端缓存(keep-alive)
Oct 12 Javascript
在Layui 的表格模板中,实现layer父页面和子页面传值交互的方法
Sep 10 Javascript
VUE+elementui面包屑实现动态路由详解
Nov 04 Javascript
vue中template的三种写法示例
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
关于PHPDocument 代码注释规范的总结
2013/06/25 PHP
php对象和数组相互转换的方法
2015/05/12 PHP
php获取当前月与上个月月初及月末时间戳的方法
2016/12/05 PHP
根据key删除数组中指定的元素实现方法
2017/03/02 PHP
全面解析PHP面向对象的三大特征
2017/06/10 PHP
JavaScript中的作用域链和闭包
2012/06/30 Javascript
JS onmousemove鼠标移动坐标接龙DIV效果实例
2013/12/16 Javascript
用于deeplink的js方法(判断手机是否安装app)
2014/04/02 Javascript
每天一篇javascript学习小结(Date对象)
2015/11/13 Javascript
Jquery Ajax Error 调试错误的技巧
2015/11/20 Javascript
Bootstrap中点击按钮后变灰并显示加载中实例代码
2016/09/23 Javascript
js调用屏幕宽度的简单方法
2016/11/14 Javascript
JS正则表达式修饰符中multiline(/m)用法分析
2016/12/27 Javascript
详解ECMAScript6入门--Class对象
2017/04/27 Javascript
vue 属性拦截实现双向绑定的实例代码
2018/10/24 Javascript
详解Vue+elementUI build打包部署后字体图标丢失问题
2020/07/13 Javascript
用Python的urllib库提交WEB表单
2009/02/24 Python
Python下的常用下载安装工具pip的安装方法
2015/11/13 Python
Pyinstaller将py打包成exe的实例
2018/03/31 Python
用Python分析3天破10亿的《我不是药神》到底神在哪?
2018/07/12 Python
python 实现百度网盘非会员上传超过500个文件的方法
2021/01/07 Python
英国在线珠宝店:The Jewel Hut
2017/03/20 全球购物
Ancheer官方户外和运动商店:销售电动自行车
2019/08/07 全球购物
初中语文教学反思
2014/02/02 职场文书
六查六看剖析材料
2014/02/15 职场文书
酒店优秀员工事迹材料
2014/06/02 职场文书
学校关爱留守儿童活动方案
2014/08/27 职场文书
初中家长评语和期望
2014/12/26 职场文书
投标邀请书范本
2015/02/02 职场文书
生日寿星公答谢词
2015/09/29 职场文书
七年级语文教学反思
2016/03/03 职场文书
导游词之山西关帝庙
2019/11/01 职场文书
六年级上册《闻官军收河南河北》的教学设计
2019/11/15 职场文书
Android开发EditText禁止输入监听及InputFilter字符过滤
2022/06/10 Java/Android
MySQL聚簇索引和非聚簇索引的区别详情
2022/06/14 MySQL
使用HBuilder制作一个简单的HTML5网页
2022/07/07 HTML / CSS