Nodejs-connect 中间件

待补充,原文链接:https://github.com/alsotang/node-lessons/tree/master/lesson18

HTTP

Nodejs 的经典 httpServer 代码

var http = require('http');

var server = http.createServer(requestHandler);
function requestHandler(req, res) {
  res.end('hello visitor!');
}
server.listen(3000);

里面的函数 requestHandler 就是所有http请求的响应函数,即所有的请求都经过这个函数的处理,是所有请求的入口函数。

通过 requestHandler 函数我们能写一些简单的 http 逻辑,比如上面的例子,所有请求都返回 hello visitor!

然而,我们的业务逻辑不可能这么简单。例如:需要实现一个接口,要做的是当请求过来时,先判断来源的请求是否包含请求体,然后判断请求体中的id是不是在数据库中存在,最后若存在则返回true,不存在则返回false。

1. 检测请求中请求体是否存在,若存在则解析请求体;
1. 查看请求体中的id是否存在,若存在则去数据库查询;
1. 根据数据库结果返回约定的值;

我们首先想到的,抽离函数,每个逻辑一个函数,简单好实现低耦合好维护。

实现代码:

function parseBody(req, callback) {
  //根据http协议从req中解析body
  callback(null, body);
}
function checkIdInDatabase(body, callback) {
  //根据body.id在Database中检测,返回结果
  callback(null, dbResult);
}
function returnResult(dbResult, res) {
  if (dbResult && dbResult.length > 0) {
    res.end('true');
  } else {
    res.end('false')
  }
}
function requestHandler(req, res) {
  parseBody(req, function(err, body) {
    checkIdInDatabase(body, function(err, dbResult) {
      returnResult(dbResult, res);
    });
  });
}

上面的解决方案解决了包含三个步骤的业务问题,出现了3个 }); 还有3个 err 需要处理,上面的写法可以得达到预期效果。

然而,业务逻辑越来越复杂,会出发展成30个回调逻辑,那么就出现了30个 }); 及30个 err异常。更严重的是,到时候写代码根本看不清自己写的逻辑在30层中的哪一层,极其容易出现 多次返回 或返回地方不对等问题,这就是 回调金字塔 问题了。

大多数同学应该能想到解决回调金字塔的办法,朴灵的《深入浅出Node.js》里讲到的三种方法。下面列举了这三种方法加上ES6新增的Generator,共四种解决办法。

  • EventProxy —— 事件发布订阅模式(第四课讲到)
  • BlueBird —— Promise方案(第十七课讲到)
  • Async —— 异步流程控制库(第五课讲到)
  • Generator —— ES6原生Generator

理论上,这四种都能解决回调金字塔问题。而Connect和Express用的是 类似异步流程控制的思想 。

关于异步流程控制库下面简要介绍下,或移步@第五课。 异步流程控制库首先要求用户传入待执行的函数列表,记为funlist。流程控制库的任务是让这些函数 顺序执行 。

callback是控制顺序执行的关键,funlist里的函数每当调用callback会执行下一个funlist里的函数

我们动手实现一个类似的链式调用,其中 funlist 更名为 middlewarescallback 更名为 next,码如下:

var middlewares = [
  function fun1(req, res, next) {
    parseBody(req, function(err, body) {
      if (err) return next(err);
      req.body = body;
      next();
    });
  },
  function fun2(req, res, next) {
    checkIdInDatabase(req.body.id, function(err, rows) {
      if (err) return next(err);
      res.dbResult = rows;
      next();
    });
  },
  function fun3(req, res, next) {
    if (res.dbResult && res.dbResult.length > 0) {
      res.end('true');
    }
    else {
      res.end('false');
    }
    next();
  }
]

function requestHandler(req, res) {
  var i=0;

  //由middlewares链式调用
  function next(err) {

    if (err) {
      return res.end('error:', err.toString());
    }

    if (i<middlewares.length) {
      middlewares[i++](req, res, next);
    } else {
      return ;
    }
  }

  //触发第一个middleware
  next();
}

上面用middlewares+next完成了业务逻辑的 链式调用,而middlewares里的每个函数,都是一个 中间件

整体思路是:

  1. 将所有 处理逻辑函数(中间件) 存储在一个list中;
  2. 请求到达时 循环调用 list中的 处理逻辑函数(中间件)

Connect的实现

Connect的思想跟上面阐述的思想基本一样,先将处理逻辑存起来,然后循环调用。

Connect中主要有五个函数 PS: Connect的核心代码是200+行,建议对照源码看下面的函数介绍。

函数名 作用
createServer 包装httpServer形成app
listen 监听端口函数
use 向middlewares里面放入业务逻辑
handle 上一章的requestHandler函数增强版
call 业务逻辑的真正执行者

Express

大家都知道Express是Connect的升级版。

Express不只是Connect的升级版,它还封装了很多对象来方便业务逻辑处理。Express里的Router是Connect的升级版。

Express大概可以分为几个模块

模块 描述
router 路由模块是Connect升级版
request 经过Express封装的req对象
response 经过Express封装的res对象
application app上面的各种默认设置

简要介绍一下每个模块







你可能感兴趣的:(Nodejs-connect 中间件)