浅谈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 相关文章推荐
js监听输入框值的即时变化onpropertychange、oninput
Jul 13 Javascript
JS编程小常识很有用
Nov 26 Javascript
jquery实现省市select下拉框的替换(示例代码)
Feb 22 Javascript
JavaScript中的值类型详细介绍
Dec 29 Javascript
教你JS中的运算符乘方、开方及变量格式转换
Aug 09 Javascript
jquery获取点击控件的绝对位置简单实例
Oct 13 Javascript
图解Javascript——作用域、作用域链、闭包
Mar 21 Javascript
js获取文件里面的所有文件名(实例)
Oct 17 Javascript
vue.js整合vux中的上拉加载下拉刷新实例教程
Jan 09 Javascript
AngularJs用户输入动态模板XSS攻击示例详解
Apr 21 Javascript
JavaScript简易计算器制作
Jan 17 Javascript
通过实例解析jQ Ajax操作相关原理
Sep 23 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
CodeIgniter与PHP5.6的兼容问题
2015/07/16 PHP
PHP使用socket发送HTTP请求的方法
2016/02/14 PHP
PHP连接MySQL数据库操作代码实例解析
2020/07/11 PHP
JavaScript开发时的五个注意事项
2007/12/08 Javascript
jquery使用ColorBox弹出图片组浏览层实例演示
2013/03/14 Javascript
jQuery查询数据返回object和字符串影响原因是什么
2013/08/09 Javascript
深入理解JavaScript系列(29):设计模式之装饰者模式详解
2015/03/03 Javascript
jQuery内容折叠效果插件用法实例分析(附demo源码)
2016/04/28 Javascript
Node.js的npm包管理器基础使用教程
2016/05/26 Javascript
JS判断日期格式是否合法的简单实例
2016/07/11 Javascript
微信小程序开发之选项卡(窗口底部TabBar)页面切换
2017/04/12 Javascript
关于Angular2 + node接口调试的解决方案
2017/05/28 Javascript
jQuery dateRangePicker插件使用方法详解
2017/07/28 jQuery
vue-router 组件复用问题详解
2018/01/22 Javascript
详解VUE Element-UI多级菜单动态渲染的组件
2019/04/25 Javascript
[04:50]2019DOTA2高校联赛秋季赛四强集锦
2019/12/27 DOTA
python实现的简单文本类游戏实例
2015/04/28 Python
举例讲解Python设计模式编程中对抽象工厂模式的运用
2016/03/02 Python
详解Python中类的定义与使用
2017/04/11 Python
Python之读取TXT文件的方法小结
2018/04/27 Python
Python 2/3下处理cjk编码的zip文件的方法
2019/04/26 Python
Python面向对象进阶学习
2019/05/21 Python
Ubuntu下Anaconda和Pycharm配置方法详解
2019/06/14 Python
Python使用指定端口进行http请求的例子
2019/07/25 Python
Windows+Anaconda3+PyTorch+PyCharm的安装教程图文详解
2020/04/03 Python
解析HTML5中的新功能本地存储localStorage
2016/03/01 HTML / CSS
世界知名接发和假发品牌:Poze Hair
2017/03/08 全球购物
JD Sports比利时官网:英国领先的运动鞋和运动服饰零售商
2018/10/10 全球购物
机械电子工程毕业生自荐信
2013/11/23 职场文书
大学毕业感言
2014/01/10 职场文书
家居饰品店创业计划书
2014/01/31 职场文书
庆元旦迎新年广播稿
2014/02/18 职场文书
中学生秋季运动会广播稿
2014/09/21 职场文书
银行柜员与客户起冲突检讨书
2014/09/27 职场文书
营销与策划实训报告
2014/11/05 职场文书
2016年寒假政治学习心得体会
2015/10/09 职场文书