express中间件原理 && 实现

Posted Wayne Zhu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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中间件的简单使用。 

 

 

 

 

 

 

 

 

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

 

 

 

 

 

 

 

 

 

 

 

 

 

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

以上是关于express中间件原理 && 实现的主要内容,如果未能解决你的问题,请参考以下文章

失败的尝试 10. regular expression matching & 正则

Express中间件的使用原理及实现

这可能是最详细的分布式锁设计方案了

Express中间件简单的实现原理

中间件安全Tomcat&Nginx解析&爆破&CVE漏洞

黑马Android视频教程——58_异步http框架简介&实现原理