Simple application and implementation of express middleware

Simple application and implementation of express middleware

After seeing double the net Mu class teacher's class in conjunction with their understanding did some simple conclusion, if any impropriety, please correct me.

Mentioned expresswould have to mention middleware, followed by a brief introduction about the expressimple application middleware with some commonly used functions.

1. express simple application middleware

In the daily development project, login authentication is a very common scenario, this time expressmiddleware can come in handy. Next are using native nodeand expressmethod middleware simple login authentication.

Scenario: before acquiring the blog list for login authentication, only can get a list of blog logged in.

Native node.jsrealized login authentication:

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

express Middleware login authentication:

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) => {
    获得博客列表
})

While this simple example above seem original node.jsto make it easier, but the use of middleware package better way, in some parity complex projects can reflect the advantage.

2. express the use, get, post, next, listenrealization of the method

express middleware to achieve a flow chart

The above combined flowchart and complete code is given, a brief description of the process at the implementation:

Here with use(and in the realization of the idea get, postit is the same) as an example:

  • Get the path and middleware

    useIncoming call when the user parameter to registerthe function, registerfunction parameters obtained after separating the two paths and an intermediate portion, stored in the path info.path, the middleware stored in info.stackthe. Then registerthe function of the separating path and back to the intermediate use, useafter the intermediate path and get separated, which will be assigned to constructorthe function 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函数,否则第一个需要执行的中间件也不会执行了。

    Middleware passed in the next argument, here it is the next function definition, if a middleware program before executing the next function, it will then execute stack.shiftout next middleware in the middleware needs to perform.

Attach the complete code:

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()
};

Finish

Guess you like

Origin www.cnblogs.com/zhangguicheng/p/11962809.html
Recommended