express中间件原理 && 实现

一、什么是express中间件? 

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

var express = require('express');

var app = express();

app.listen(3000, function () {
  console.log('listening 3000')
});

app.use(middleware1);
app.use(middleware2);
app.use(middleware3);

  是的, middleware1、middleware2、middleware3就是中间件了,我们使用app.use,就是在使用这个中间件。 

  即中间件的使用方法就是 app.use(middleware)。 

  那么究竟什么是中间件

  这里有一个详尽的解释: http://expressjs.com/en/guide/using-middleware.html, 大致如下:

Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.

  当一个请求发送到服务器之后,服务器接收到请求,然后开始处理,处理完之后返回响应。 处理的时候就会用到中间件了,使用中间件的顺序就是app.use(middleware)的顺序。 

  比如: 之前我在写websocket聊天室时使用的中间件大致如下:

let express = require('express')

let path = require('path')

let app = express()

// 省略若干。。。

// 用于解析 body。  
let bodyParser = require("body-parser");

// 基于express实例创建http服务器
let server = require('http').Server(app);

// 创建websocket服务器,以监听http服务器
let io = require('socket.io').listen(server);

// 引入路由
let route = require('../router/index.js');

// 使用devMiddleware
app.use(devMiddleware)

// 使用hotMiddleware
app.use(hotMiddleware)

// 使用使用了body-parser模块,才能通过 req.body 接受到post表单里的内容。
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

// node你服务器使用的静态文件
app.use('/', express.static('./www'))

// 使用路由
app.use('/', route);

// 开启node后台服务器
console.log('> Starting dev server...')
let port = '3000';
server.listen(port, function () {
    console.log('The server is listening at localhost:' + port)
});


app.get('*', function (request, response){
  response.sendFile(path.resolve(__dirname,  '../www/index.html'))
})


// 引入服务器端websocket处理代码
let Websocket = require('./websocket.js');

// 执行
Websocket(io);

  这里为了表示清楚,省略了一些代码,然后可以看到服务器端接收到请求之后使用的顺序大致如下:

  1. 使用devMiddleware
  2. 使用hotMiddleware
  3. 使用body-parser解析
  4. 使用静态文件
  5. 使用路由

  这里的顺序还是很清楚的,即请求来了之后,会依次通过各个中间件进行处理,处理完成之后,就next(),把控制权交给下一个中间件,到了最后,我们就可以很好的使用路由了。比如之前使用了body-parser中间件,后面在路由中我们就可以使用 req.body 来处理了。

二、为什么要使用express中间件(好处)? 

  我们可以总结为下面的几个好处:

  • 逻辑清楚,层次分明。 正如TCP/IP中的分层一样,通过分层,可以使得每一个部分各司其职,更好的干事情。
  • 便于维护。如果觉得其中一个做的不好,还可以换一个中间件,而其他的不用替换。
  • 可复用。我们写好了一个中间件之后,就可以直接拿来在别的地方用了,就比如,我们在使用第三方中间件的时候,直接npm install somemiddleware,然后 require,最后直接 app.use 即可,非常方便。

三、中间件的本质是什么? 

   实际上,中间件就是一个函数,这个函数可以接受三个参数,req、res、next, 其中req即客户端发送过来的请求,res即可以进行相应,next即后面还有其他的中间件,通过next可以交出req、res的控制权,后续的中间件继续处理。

     如下所示:

function middleware(req,res,next){
    // 对req、res进行处理

    // 如果后面还有中间件,那么就交出控制权,后面的中间件继续对请求进行处理。
    next();
}

     另外,next实际上也是一个函数,这里通过next()调用,就可以成功地交出控制权了。 

   我们可以建立一个express,然后在index.js中代码如下:

var express = require('express');

var app = express();

app.use(function (req, res, next) {
  console.log(req)
  next();
});

app.get('/', function (req, res) {
  res.send('哈哈');
  res.end();
});

app.listen(3000, function () {
  console.log('listening 3000')
});

  这时,可以发现: 当我们启动服务器的时候,请求localhost:3000, 然后在后台就会打印出这个请求的详细信息,然后,next(),就可以接着被get这个中间件处理,向客户端发送“哈哈”, 最后,res.end(),即返回响应,后面不再有中间件了。 

  注意:如果在第一个中间中没有next(), 那么这个请求就会被 hang ,后续的get中间件就无法使用了,所以,对于中间位置的中间件必须调用next()函数把这个控制权交出来。对于一些HotMiddleware和devMilddleware,他们也都是在最后需要交出控制权的。 

  

四、express中间件的分类。

  那么express中间件是如何分类的呢?

  一般,express应用可以使用下面的几类中间件:

  • Application-level middleware 应用级中间件
  • Router-level middleware 路由级中间件
  • Error-handling middleware 错误处理级中间件
  • Built-in middleware 内置中间件
  • Third-party middleware 第三方中间件   

  

4.1 Application-level middleware

  

var app = express()

app.use(function (req, res, next) {
  console.log('Time:', Date.now())
  next()
})

这就是一个应用级的中间件,即对于请求,我们可以对之进行相应的处理。

app.use('/user/:id', function (req, res, next) {
  console.log('Request Type:', req.method)
  next()
})

这个例子是当相应的请求执行时,我们所做的处理。

4.2 Router-level middleware

  路由级中间件和一般的应用级中间件的使用方法是一样:

var router = express.Router()

  下面是一个例子:

var app = express()
var router = express.Router()

// a middleware function with no mount path. This code is executed for every request to the router
router.use(function (req, res, next) {
  console.log('Time:', Date.now())
  next()
})

// a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path
router.use('/user/:id', function (req, res, next) {
  console.log('Request URL:', req.originalUrl)
  next()
}, function (req, res, next) {
  console.log('Request Type:', req.method)
  next()
})

// a middleware sub-stack that handles GET requests to the /user/:id path
router.get('/user/:id', function (req, res, next) {
  // if the user ID is 0, skip to the next router
  if (req.params.id === '0') next('route')
  // otherwise pass control to the next middleware function in this stack
  else next()
}, function (req, res, next) {
  // render a regular page
  res.render('regular')
})

// handler for the /user/:id path, which renders a special page
router.get('/user/:id', function (req, res, next) {
  console.log(req.params.id)
  res.render('special')
})

// mount the router on the app
app.use('/', router)

4.3 Error-handling middleware

       错误处理中间件总是会有四个参数,所以你必须提供四个参数来表明这个中间件是一个错误处理中间件,即使也许你不需要Next对象,你也得明确说明,否则的话,这就会被解析为一个普通的中间件,而非错误处理中间件了。

    下面就是一个错误处理中间件了:

app.use(function (err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})

   

4.4 Built-in middleware

  从版本4.4开始,express已经不再依赖于 connect 。 

   Express中唯一的一个内置中间件就是 express.static 了, 这个函数是基于 server-static 的,负责提供类似于html、css、img、js等静态文件的。 

   这个内置中间件的使用方式如下:

express.static(root, [options])

   root就是提供静态文件的目录。

  下面是一个简单的使用express.static中间件的例子:

var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now())
  }
}

app.use(express.static('public', options))

  在一个app里,你还可以使用多个静态文件目录:

app.use(express.static('public'))
app.use(express.static('uploads'))
app.use(express.static('files'))

  

4.5 Third-party middleware

   第三方中间件我们使用的非常多,比如cookie-parser、body-parser等等,下面是一个使用了第三方中间件的例子:

var express = require('express')
var app = express()
var cookieParser = require('cookie-parser')

// load the cookie-parsing middleware
app.use(cookieParser())

五、express中间件的简单使用。 

   如下:

var express = require('express');

var app = express();

app.listen(3000, function () {
  console.log('listening 3000')
});

function middleware1(req, res, next) {
  console.log('middleware1 before next');
  next();
  console.log('middleware1 after next');
}

function middleware2(req, res, next) {
  console.log('middleware2 before next');
  next();
  console.log('middleware2 after next');
}

function middleware3(req, res, next) {
  console.log('middleware3 before next');
  next();
  console.log('middleware3 after next');
}

app.use(middleware1);
app.use(middleware2);
app.use(middleware3);

然后运行:

 可以看到,中间件处理的过程就是app.use()的顺序,另外,在执行之后,还会执行next()之后的语句,应该是入栈、出栈的执行过程。

六、实现一个简单的express中间件。

function express() {
  var middlewares = [];

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

    function next() {
      var task = functions[i++];
      if (!task) {
        return;
      }
      task(req, res, next);
    }

    next();
  }

  app.use = function (task) {
    functions.push(task);
  }

  return app;
}

通过middlewares数组来存储所有的中间件,然后建立next()函数,即执行一次,调用一个中间件函数。 

使用方式如下:

var http = require('http');

var app = express();
http.createServer(app).listen('3000', function () {
    console.log('listening 3000....');
});

function middleware1(req, res, next) {
    console.log('middleware1 before next()');
    next();
    console.log('middleware1 after next()');
}

function middleware2(req, res, next) {
    console.log('middleware2 before next()');
    next();
    console.log('middleware2 after next()');
}

function middleware3(req, res, next) {
    console.log('middleware3 before next()');
    next();
    console.log('middleware3 after next()');
}

app.use(middleware1);
app.use(middleware2);
app.use(middleware3);

 以上。

参考文章: http://expressjs.com/en/guide/using-middleware.html

原文地址:https://www.cnblogs.com/zhuzhenwei918/p/7452434.html