最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 多图理解 Express

    正文概述 掘金(发多)   2020-12-10   496

    前置内容

    Node 可以简单快速地开启 Http 服务:

    const http = require("http");
    
    // 创建http服务
    const server = http.createServer((req, res) => {
      res.end("hello Node");
    });
    // 监听端口
    server.listen(3000, () => {
      console.log("服务已启动");
    });
    

    这样一个 Node 服务就启动了,此时浏览器访问http://127.0.0.1:3000会返回hello Node,但是目前还没有路由,所以你会发现无论输入http://127.0.0.1:3000/hi或是其他路径都会返回相同的结果。因此 Node 服务端框架 express 提供了路由中间件请求处理等功能帮助我们高效的提供服务。

    启动 express 服务

    我们借助一个例子,来理解 express 如何启动服务

    const express = require("express");
    const app = express();
    
    // 开启express服务
    app.listen("3000", () => {
      console.log("服务已运行在3000端口上");
    });
    

    多图理解 Express

    • express 是一个函数,执行 express 会返回一个 app 函数,app 就被作为http.createServer的处理函数

    • 调用 app.listen 可以开启服务,本质还是通过http.createServer(app)开启 Node 服务

    • 观察 app 这个处理函数,它在原来参数基础上扩展了next函数,next可以控制下一个handle或者中间件的调用,并且将具体逻辑处理交给app.handle,实际上这是 express 中非常常见的代理模式,app 提供了 express 服务中所有请求唯一的入口

    多图理解 Express

    注册路由

    const express = require("express");
    const app = express();
    
    app.get("/", (req, res, next) => {
      res.end("hello");
    });
    
    // 开启express服务
    app.listen("3000", () => {
      console.log("服务已运行在3000端口上");
    });
    

    express 只需要调用app.get就可以完成 get 请求方法的路由注册,express 中路由注册的内部执行流程涉及到最重要的四个模块:app、router、route、layer,其层次为:

    多图理解 Express

    • app层是express中的顶层设计,持有一个router路由对象
    • router层存放所有的路由信息,内部会持有layer存放在自己的栈中
    • layer在express中主要有三种(express中被统一抽象成了layer类):
      • route layer:持有route,保存在router中,
      • middleware layer:不会持有route,只会持有相应的handle函数,保存在router中,
      • handle layer:持有路由最终的handle,保存在route

    以上我们对express中各模块的职责了解一番后,app.get路由注册的流程会是这样:

    多图理解 Express

    1. app.get会调用router的route方法,这个方法会生成一个route和一个route layer,让route layer持有routeroute提供route layer一个dispatch方法作为handle,最后压入router的栈中
    2. 将所有的handle函数传递给新生成的route,route.get会生成handle layer保存相应的handle,并存放在自己的栈中

    再通过源码来看看express具体实现:

    // application.js
    
    // methods是所有http方法集合,这里定义了app.get、app.post等方法
    methods.forEach(function(method){
      app[method] = function(path){
        if (method === 'get' && arguments.length === 1) {
          // app.get(setting)
          return this.set(path);
        }
    
        // 懒初始化router对象
        this.lazyrouter();
    
        // 执行第一步,执行router的route方法
        var route = this._router.route(path);
        // 执行第二步,调用新创建的route.get方法,将handle传递出去
        route[method].apply(route, slice.call(arguments, 1));
        return this;
      };
    });
    
    
    // router.js
    proto.route = function route(path) {
      // 生成一个新route
      var route = new Route(path);
    
      // 生成一个route layer
      var layer = new Layer(path, {
        sensitive: this.caseSensitive,
        strict: this.strict,
        end: true
      }, route.dispatch.bind(route));
    
      // 让layer持有route
      layer.route = route;
    
      // 将layer推入到栈中
      this.stack.push(layer);
      
      // 返回route对象
      return route;
    };
    
    // route.js
    methods.forEach(function(method){
      Route.prototype[method] = function(){
        // 打平数组数据结构
        var handles = flatten(slice.call(arguments));
    
        // 遍历所有处理函数
        for (var i = 0; i < handles.length; i++) {
          var handle = handles[i];
    
          if (typeof handle !== 'function') {
            var type = toString.call(handle);
            var msg = 'Route.' + method + '() requires a callback function but got a ' + type
            throw new Error(msg);
          }
    
          // 创建一个handle layer, 每个handle layer都会保存一个handle函数
          var layer = Layer('/', {}, handle);
          layer.method = method;
    
          this.methods[method] = true;
          // handle layer被推入到route的栈中
          this.stack.push(layer);
        }
    
        return this;
      };
    });
    

    命中路由

    由于我们通过app.get注册了一个路由,此时浏览器访问http://127.0.0.1:3000/会有相应的响应,但访问其他地址不会有响应,这表明路由已经在工作了。

    现在命中路由的流程是这样:

    多图理解 Express

    1. 根据前文我们知道express开启的服务所有请求都会走到app的逻辑中,app则执行app.handle方法,app.handle又把处理具体逻辑交给router.handle来实现,相当于把请求转给router层来处理
    2. 请求到了router层,router.handle会遍历自己stack中所有的layer(route layer和middleware layer, 例子中为route layer),并根据当前请求的url地址依次匹配注册时保存的路由path,当条件命中时会执行该layerhandle_request方法,这个方法就是注册时绑定在route layer上对应的route的dispatch方法
    3. route.dispatch的逻辑和router.handle相似,遍历执行自己stack中的handle layer,执行其handle_request方法,即执行注册路由时绑定的handle函数。这里express将调用下一个layer.handle_request交给了开发者,如果有多个handle layer,开发者可以显示使用express提供的函数(一般是next)来实现各个handle调用的逻辑

    中间件

    中间件注册

    中间件是Web服务端框架的重要模块,服务端常常把系统中相同的逻辑交给中间件完成,从而实现认证鉴权日志错误处理数据转换等功能,express中中间件就是一个函数,我们可以使用app.use来添加一个中间件。

    const express = require("express");
    const app = express();
    
    app.use(function(req, res, next) {
      console.log('当前请求地址:', req.url);
      // 注意,要显式调用next,否则请求/不会被匹配
      next()
    })
    
    app.get("/", (req, res, next) => {
      res.end("hello");
    });
    
    // 开启express服务
    app.listen("3000", () => {
      console.log("服务已运行在3000端口上");
    });
    

    我们创建了一个打印请求路径的中间件,express注册中间件的内部流程是:

    多图理解 Express

    1. app.use将所有handles处理函数传递给router.use方法
    2. router.use会遍历所有handles,每个handle都会创建一个middleware layer,将其保存在middleware layer上,与route layer不同的是,该layer不会持有route,最后将其压入router的栈中

    中间件命中

    实际上中间件还支持添加路径即app.use(path, ...fns),不加路径则是一个全局的中间件,能响应所有的请求,因为中间件的注册和路由注册相似,其命中过程也和路由相同,都是根据path寻找对应的layer,执行其绑定的handle函数。

    多图理解 Express

    1. app.handle将处理逻辑交给router.handle
    2. router.handle通过path找到相应的middleware layer,由于例子中我们注册的是全局中间件,则会命中该middleware layer,将其取出执行绑定在该layer上的handle_request方法

    路径匹配

    express支持正则作为路径注册路由,可以使用以下几种方式:

    • app.get('some?path', handle) // 字符串正则
    • app.get('somepath/:id', handle) // 路径带参数
    • app.get(/somepath/, handle) // 支持路径为正则表达式

    这些特性主要得益于path-to-regexp的支持,express中路由的命中是根据注册时保存的正则进行的。

    多图理解 Express

    总结

    express把app设计为顶层结构,将app作为接口层暴露出去供开发者使用,内部将app的usegethandle方法具体处理逻辑交给router,相当于app是一层向外暴露的代理。同时express使用while可中断的路由匹配方式让开发者可以使用next控制handle函数的调用,开发者在定义路由或中间件时需注意彼此的顺序。


    起源地下载网 » 多图理解 Express

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元