zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    04-增删改查
    03-编写dao实现类方式
    02-基于注解的入门案例
    .net core api 图片上传与加载
    笔记一、数据库初始化 约定
    .net core MVC中级教程(四)
    .net core MVC中级教程(三)
    Windows 盘符映射
    c# 队列和堆栈
    isValidNode
  • 原文地址:https://www.cnblogs.com/zhuzhenwei918/p/7452434.html
Copyright © 2011-2022 走看看