express 中间件的简单应用与实现

看了慕课网双越老师的课之后结合自己的理解做了一些简单的总结,如有不恰当之处,欢迎指正。

提到 express 就不得不提到中间件,接下来就简单的介绍一下 expres 中间件的简单应用与部分常用函数的实现。

1. express 中间件的简单应用

在日常项目的开发中,登录验证是一个非常常见的场景,这个时候 express 中间件就可以派上用场了。接下来分别使用原生 nodeexpress 中间件的方法实现简单的登录验证。

应用场景:在获取博客列表之前要进行登录验证,只有在登录状态下才可以获取博客列表。

原生 node.js 实现登录验证:

if(method === 'GET' && req.path === '/api/blog/list') {
  // 若req.session.username有值说明已经登录
  if (req.session.username){
    // 登录状态下可以获取博客列表
    获取博客列表
  }
}

express 中间件实现登录验证:

function loginCheck(req, res, next) {
    if (某个登录成功的条件) {
       next(); // 登录成功执行next
    } else {
       res.json({
         errno: -1,
         msg: 'login fail'
    })
    }
}
// 若在登录状态下,loginCheck会执行next函数,从而执行第二个中间件,获取博客列表。
app.get('/api/blog/list', loginCheck, (req, res, next) => {
    获得博客列表
})

虽然上面这个简单的例子中看上去似乎原生 node.js 要更简单一些,但是使用中间件的方式封装性更好,在一些比价复杂的项目中就能体现出优势了。

2. express 中 use, get, post, next, listen 等方法的实现

结合上面的流程图和最后给出的完整代码,简单说明一下实现的过程:

这里以 use (在实现思路上和 get, post 是一样的)为例说明:

  • 获取路径和中间件

    use 将用户调用时传入的参数传给 register 函数,register 函数将获取到参数后分离出路径和中间两部分,路径存放在 info.path 中,中间件存放在 info.stack 中。然后 register 函数将分离开的路径和中间件再返回给 useuse 拿到分离后的路径和中间件之后,会将其赋值给 constructor 函数中 this.routes.all

注意:register 在分离路径时需要注意的一点是对于没有路径只有中间件的参数会将其路径赋值为根路径 /

  • listen 方法的实现

    使用 express 时调用 app.listen(3000) 实际上不只是监听 3000 端口还创建了一个 http 服务,参数是一个回调函数,代码如下:

    listen(...args) {
      const server = http.createServer(this.callback());
      server.listen(...args);
    }
  • res.json 方法的实现

    在回调函数 callback 中首先实现一个 res.json 方法,实现方式也比较简单。

    1. 定义 resHeaderContent-typeapplication/json
    2. 执行res.end({JSON.stringify(data)}) 将用户传入的 data 对象转换为 JSON 字符串输出到屏幕上。
  • 获取需要执行的中间件

    这部分通过 match 函数实现。通过 req.urlreq.method.toLowerCase 来获取需要执行的中间件。中间实现的方法就有很多了,思路就是通过method 找出相应的路径和中间件,这个已经在 this.routes 中分类存储了。然后找出路径和中间件之后利用 indexOf 方法,

    if (url.indexOf(routeInfo.path) === 0) 如果 if 判断成立说明满足条件,那么需要执行的中间件也就找出来了。

  • app.next 方法的实现

    这部分通过 handle 函数实现。思路:定义一个 next 函数,在那些需要执行的中间件中,先取出第一个,如果不为空那么开始执行,三个参数分别为 req, res, next。后面需要执行的中间件到底执不执行取决于前一个中间件的程序中是否调用了 next 方法。代码如下:

     const next = () => {
          // shift 取得数组的第一项
          const middleware = stack.shift();
          if (middleware) {
            // 执行中间件函数
            middleware(req, res, next);
          }
        };
        // 定义完后立即执行
        next();

    注意:在定义完next函数之后,必须立即执行一次next函数,否则第一个需要执行的中间件也不会执行了。

    中间件中传入的参数中的next,就是在这里定义的next函数,如果前一个中间件程序中执行了next函数,那么会再执行 stack.shift 取出需要执行的中间件中的下一个中间件。

附上完整代码:

const http = require('http');
const slice = Array.prototype.slice;

class LikeExpress {
  constructor() {
    // 存放路径和中间件,all中存放的是调用use传入的路径和中间件
    this.routes = {
      all: [],
      get: [],
      post: []
    }
  }

  // 分离出路径和中间件,info.path中存放路径,info.stack 中存放中间件
  register(path) {
    const info = {};
    if (typeof path === 'string') {
      info.path = path;
      // info.stack 存放所有的中间件
      // 如果第一个参数是路由在取中间件时就要从数组的第2个位置开始取
      // slice.call(arguments, 1) 的作用就是取arguments从第二个位置开始之后的所有元素都取出来并变成数组的形式。
      info.stack = slice.call(arguments, 1);
    } else {
      // 如果第一个参数不是一个路由,那么我们就假定第一个参数是一个根路由
      info.path = '/';
      info.stack = slice.call(arguments, 0);
    }
    return info;
  }

  use() {
    // 实际使用时,参数是通过use传递进来的
    // 将所有的参数传入到register函数中
    const info = this.register.apply(this, arguments);
    // info 是一个对象,info.path 中存放的是路径,info.stack 中存放的是中间件
    this.routes.all.push(info);

  }

  get() {
    const info = this.register.apply(this, arguments);
    this.routes.get.push(info);

  }

  post() {
    const info = this.register.apply(this, arguments);
    this.routes.post.push(info);
  }

  // 匹配use,get或post方法会执行的中间件
  match(method, url) {
    let stack = [];
    if (url === '/favicon.ico') {
      return stack;
    }
    // 获取routes
    let curRoutes = [];
    // concat 是数组中的一个方法,如果没有参数,那么会生成一个当前数组的副本并将其赋值给前面的变量,如果有参数会将参数加入到生成的副本的后面然后将其赋值给变量
    // 如果是use,那么就把use中的路径和中间列表复制到curRoutes中
    // 如果方法是get或post那么下面这句话,由于this.routes.all是undefined,所以会将当前curRoutes生成一个副本赋值给curRoutes,其实一点变化都没有
    curRoutes = curRoutes.concat(this.routes.all);
    // 如果是get或post,那么就把相应的路径和中间件复制到curRoutes中
    curRoutes = curRoutes.concat(this.routes[method]);

    curRoutes.forEach(routeInfo => {
      // url='/api/get-cookie' routeInfo.path='/'
      // url='/api/get-cookie' routeInfo.path='/api'
      // url='api/get-cookie' routeInfo.path='/api/get-cookie'
      if (url.indexOf(routeInfo.path) === 0) {
        // 匹配成功
        stack = stack.concat(routeInfo.stack);
      }
    });
    return stack;
  }

  // 核心的 next 机制
  handle(req, res, stack) {
    const next = () => {
      // 拿到第一个匹配的中间件
      // shift 取得数组的第一项
      const middleware = stack.shift();
      if (middleware) {
        // 执行中间件函数
        middleware(req, res, next);
      }
    };
    // 定义完后立即执行
    next();

  }

  // callback是一个(req, res) => {} 的函数,结合http-test中的代码去理解
  callback() {
    return (req, res) => {
      // res.json 是一个函数,在express中使用时传入一个对象即可在屏幕中显示出来,这里实现了这个功能
      res.json = (data) => {
        res.setHeader('Content-type', 'application/json');
        res.end(
          JSON.stringify(data)
        );
      };

      const url = req.url;
      const method = req.method.toLowerCase();
      // 找到需要执行的中间件(通过路径和method,有可能一个需要执行的中间件也没有)
      const resultList = this.match(method, url);
      this.handle(req, res, resultList);
    }
  }

  // express 中listen的作用不仅仅是监听端口,还要创建服务器
  listen(...args) {
    const server = http.createServer(this.callback());
    server.listen(...args);
  }
}

module.exports = () => {
  return new LikeExpress()
};

express 中间件的简单应用与实现的更多相关文章

  1. Express中间件简单的实现原理

    上一篇理解Express的使用之后, 再总结一篇Express中间件的简单实现原理. 我们知道Express中间件就是一个个的函数, 那么怎么让这些函数有序的执行呢? 那就需要我们调用 next 函数 ...

  2. express中间件原理 && 实现

    一.什么是express中间件? 什么是express中间件呢? 我们肯定都听说过这个词,并且,如果你用过express,那么你就一定用过express中间件,如下: var express = re ...

  3. 1.express中间件的简介

    express中间件的意思 1, 中间件是一个模块.在js中,模块意味着函数,所以中间件是一个函数.那么这个函数长什么样子? 这还要从中间件的功能说起,它拦截http 服务器提供的请求和响应对象,执行 ...

  4. Express中间件的原理及实现

    在Node开发中免不了要使用框架,比如express.koa.koa2拿使用的最多的express来举例子开发中肯定会用到很多类似于下面的这种代码 var express = require('exp ...

  5. node的express中间件之session

    虽然session与cookie是分开保存的.但是session中的数据经过加密处理后默认保存在一个cookie中.因此在使用session中间件之前必须使用cookieParser中间件. app. ...

  6. Express中间件原理详解

    前言 Express和Koa是目前最主流的基于node的web开发框架,他们的开发者是同一班人马.貌似现在Koa更加流行,但是仍然有大量的项目在使用Express,所以我想通过这篇文章说说Expres ...

  7. Node.js连接Mysql,并把连接集成进Express中间件中

    引言 在node.js连接mysql的过程,我们通常有两种连接方法,普通连接和连接池. 这两种方法较为常见,当我们使用express框架时还会选择使用中间express-myconnection,可以 ...

  8. vue+webpack+express中间件接口使用

    环境:vue 2.9.3; webpack 目的:接口的调用 跨域方式: 1.express中间的使用 2.nginx代理 3.谷歌浏览器跨域设置 -------------------------- ...

  9. express 中间件

    [express 中间件] 中间件(Middleware) 是一个函数,它可以访问请求对象(request object (req)), 响应对象(response object (res)), 和 ...

随机推荐

  1. C# - SPC(Statistical Process Control)系统 - 6西格玛数据决策和Chart模块的开发与实现

    Statistical Process Control 简介 统计过程控制(Statistical Process Control)是一种借助数理统计方法的过程控制工具.它对生产过程进行分析评价,根据 ...

  2. 用图解&&实例讲解php是如何实现websocket实时消息推送的

    WebSocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议. 以前的推送技术使用 Ajax 轮询,浏览器需要不断地向服务器发送http请求来获取最新的数据,浪费很多的带 ...

  3. day 18 random模块 时间模块 sys模块 os模块

    import random 利用random模块可以进行从一个列表或者数字范围之间随机取出一个数字 # 取随机小数 : 数学计算 print(random.random()) # 取0-1之间的小数 ...

  4. 多进程使用同一log4j配置导致的日志丢失与覆盖问题

    最近接手了一个流传很多手的魔性古早代码,追日志时发现有明显缺失.对log4j不熟,不过可以猜测日志出问题肯定和多进程使用同一个log4j配置有关.经多次排查,终于捋清了其中逻辑.本文对排查过程进行复盘 ...

  5. 剑指Offer-32.丑数(C++/Java)

    题目: 把只包含质因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含质因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 分析: ...

  6. P2415 集合求和(一道洛谷好题鸭)(虽然可以水过,但有必研究DP)

    此题坑点: 结果必须要用long long存,int存不下 如果想要像cout<<sum*pow(2,num-1)这样在输出时计算会错:long long在计算过程被隐式转换成了doubl ...

  7. 11-kubernetes RBAC 及授权

    目录 RBAC role 和 clusterrole rolebinding 和 clusterrolebinding 公共权限 clusterrole user 创建测试 创建role案例 创建 r ...

  8. 前后端vue和django配置

    django pip源更换 虚拟环境配置 目录和日志配置 数据库配置 用户模块自定义 异常,响应配置 xadmin vue vue基础配置 vue高级配置 vue this问题和样式配置

  9. MySQL 库、表、记录、相关操作(1)

    库.表.记录.相关操作(1) 数据库配置 # 通过配置文件统一配置的目的:统一管理 服务端(mysqld) .客户端(client) # 配置了 mysqld(服务端) 的编码为utf8,那么再创建的 ...

  10. 高逼格利器之Python闭包与装饰器

    生活在魔都的小明,终于攒够了首付,在魔都郊区买了一套房子:有一天,小明踩了狗屎,中了一注彩票,得到了20w,小明很是欢喜,于是想干脆用这20万来装修房子吧(decoration): 整个装修过程,小明 ...