国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

express 的 middleware 設(shè)計(jì)

zollero / 2068人閱讀

摘要:入口文件在文件夾下的,其向外界暴露了一些方法。方法也是從中繼承的。入口文件很清晰,主要是完成方法的暴露以及的一些初始化操作。下一篇寫寫路由的實(shí)現(xiàn)。

還沒(méi)用express寫過(guò)server,先把部分源碼擼了一遍,各位大神求輕拍。

express入口文件在lib文件夾下的express.js,其向外界暴露了一些方法。

最主要的(express.js 第36-47行):

function createApplication() {
  var app = function(req, res, next) {
    app.handle(req, res, next);         //各中間件的處理入口,handle方法通過(guò)mixin拓展于proto
  };

  mixin(app, EventEmitter.prototype, false);
  mixin(app, proto, false);

  app.request = { __proto__: req, app: app };
  app.response = { __proto__: res, app: app };
  app.init();
  return app;
}

exports = module.exports = createApplication;

我們經(jīng)常在自己的業(yè)務(wù)代碼中這樣寫:

    var express = require("express");
    var app = express();

其實(shí)就是調(diào)用createApplication方法.這樣就實(shí)例化了一個(gè)app。這個(gè)app比較特殊,通過(guò)mixin集成了一些其他的屬性

mixin(app, EventEmitter.prototype, false); //拓展了事件發(fā)射器原型對(duì)象
mixin(app, proto, false); //拓展了application.js中的屬性和方法

在我們業(yè)務(wù)代碼實(shí)例化app的時(shí)候,調(diào)用了app.init()方法完成了一些初始化的配置。init()方法也是從application.js中繼承的。

入口文件很清晰,主要是完成方法的暴露以及app的一些初始化操作。

接下來(lái)看下application.js中的部分代碼邏輯:

第136-146行,延遲實(shí)例化一個(gè)_router

app.lazyrouter = function lazyrouter() {
  if (!this._router) {
    this._router = new Router({
      caseSensitive: this.enabled("case sensitive routing"),
      strict: this.enabled("strict routing")
    });

    this._router.use(query(this.get("query parser fn")));
    this._router.use(middleware.init(this));
  }
};

第157-174行,這是各個(gè)middleware的入口,

app.handle = function handle(req, res, callback) {
  var router = this._router;    //獲取已經(jīng)實(shí)例化得router

  // final handler
  var done = callback || finalhandler(req, res, {
    env: this.get("env"),
    onerror: logerror.bind(this)
  });

  // no routes
  if (!router) {
    debug("no routes defined on app");
    done();
    return;
  }

  router.handle(req, res, done);    //當(dāng)http過(guò)來(lái)時(shí),對(duì)于request和response的處理從這個(gè)地方開(kāi)始
};

第187-242行,app.use方法提供了應(yīng)用級(jí)的middleware,但是事實(shí)上在214行,this.lazyrouter()新建一個(gè)route,第219-221行,然后根據(jù)app.use(fn)傳入的參數(shù)掛載到了route.use()路由級(jí)中間件上了。app.use()route.use的一個(gè)代理。

app.use = function use(fn) {
  var offset = 0;
  var path = "/";

  // default path to "/"
  // disambiguate app.use([fn])
  if (typeof fn !== "function") {
    var arg = fn;

    while (Array.isArray(arg) && arg.length !== 0) {
      arg = arg[0];
    }

    // first arg is the path
    if (typeof arg !== "function") {
      offset = 1;
      path = fn;
    }
  }

  var fns = flatten(slice.call(arguments, offset)); //鋪平arguments

  if (fns.length === 0) {
    throw new TypeError("app.use() requires middleware functions");
  }

  // setup router
  this.lazyrouter();                                //如果沒(méi)有route實(shí)例則新建一個(gè)
  var router = this._router;

  fns.forEach(function (fn) {
    // non-express app                              //如果傳入的不是express實(shí)例app
    if (!fn || !fn.handle || !fn.set) {
      return router.use(path, fn);                  //將中間件注入到router中
    }

    debug(".use app under %s", path);
    fn.mountpath = path;
    fn.parent = this;

    // restore .app property on req and res
    router.use(path, function mounted_app(req, res, next) { 
      var orig = req.app;
      fn.handle(req, res, function (err) {          
        req.__proto__ = orig.request;
        res.__proto__ = orig.response;
        next(err);
      });
    });

    // mounted an app
    fn.emit("mount", this);
  }, this);

  return this;
};

第255-258行,代理到router實(shí)例的route()的方法中:

    app.route = function route(path) {
        this.lazyrouter();
        return this._router.route(path);
    };

router實(shí)例是通過(guò)router/index.js提供構(gòu)造函數(shù)來(lái)創(chuàng)建的,在這個(gè)文件夾中第42-60行:

var proto = module.exports = function(options) {
  var opts = options || {};

  function router(req, res, next) {
    router.handle(req, res, next);    //handle方法繼承于proto
  }

  // mixin Router class functions
  router.__proto__ = proto;

  router.params = {};
  router._params = [];
  router.caseSensitive = opts.caseSensitive;
  router.mergeParams = opts.mergeParams;
  router.strict = opts.strict;
  router.stack = [];        //初始化一個(gè)stack.這個(gè)stack中保存了注冊(cè)的所有中間件

  return router;
};

提供了一個(gè)router的構(gòu)造函數(shù),它的原型對(duì)象上,第136行提供了proto.handle方法,這個(gè)方法的作用就是接收來(lái)自httpreqres

第413行,提供了proto.use方法,正如上面所說(shuō)的app.useroute.use的代理,這個(gè)方法的特殊性就在任何的http請(qǐng)求都會(huì)經(jīng)過(guò)在app.use上掛載的中間件,例如現(xiàn)在express4.x已經(jīng)將很多中間件從自身移除,需要你重新通過(guò)npm去安裝,然后在業(yè)務(wù)代碼中進(jìn)行引用,例如使用body-parser中間件:

var express = require("express");
var app = express();
var bodyParser = require("body-parser");
app.use(bodyParser.json());

這樣每次http請(qǐng)求過(guò)來(lái)的時(shí)候首先會(huì)經(jīng)過(guò)bodyParser.json()這個(gè)中間件,它提供了一個(gè)向req添加req.body = {}方法,并傳向下一個(gè)中間件的作用。
同時(shí)在route.use內(nèi)部,第439-458行,

for (var i = 0; i < callbacks.length; i++) {
    var fn = callbacks[i];

    if (typeof fn !== "function") {
      throw new TypeError("Router.use() requires middleware function but got a " + gettype(fn));
    }

    // add the middleware
    debug("use %s %s", path, fn.name || "");

    var layer = new Layer(path, {       //新建一個(gè)layer,layer上掛載了error_handler和request_handler
      sensitive: this.caseSensitive,
      strict: false,
      end: false
    }, fn);

    layer.route = undefined;            

    this.stack.push(layer);             //route自身會(huì)維護(hù)一個(gè)stack,將每個(gè)新建的layer都推入stack當(dāng)中,這個(gè)layer實(shí)例最終會(huì)對(duì)匹配的path,作出error_handle或者request_handle。
  }

第477行,proto.route方法提供了一個(gè)新建route的方法。

proto.route = function route(path) {
  var route = new Route(path);      //新建一個(gè)route,這個(gè)Route構(gòu)建函數(shù)內(nèi)部實(shí)現(xiàn)見(jiàn)./route.js,它里面提供了一個(gè)空的stack,用以

  var layer = new Layer(path, {                     //新建一個(gè)layer,layer的作用見(jiàn)下面的講解
    sensitive: this.caseSensitive,
    strict: this.strict,
    end: true
  }, route.dispatch.bind(route));

  layer.route = route;  

  this.stack.push(layer);   //新建一個(gè)route,這個(gè)route會(huì)維護(hù)自身的stack
  return route;
};

var route = require("express").Router(),但是這個(gè)方法不同的地方
在于,它會(huì)自身維護(hù)一個(gè)stack,這個(gè)stack中有你在這個(gè)方法上面定義的所有中間件。同樣,你可以通過(guò)這個(gè)route掛載對(duì)于不同路徑的req, res的處理。

使用的方法:

var express = require("express");
var app = express();
var router = express.Router();

//沒(méi)有掛載任何路徑的中間件,通過(guò)該路由的每個(gè)請(qǐng)求都會(huì)執(zhí)行該中間件
router.use(function(req, res, next) {
    console.log("route.use");
})


router.get("/test", function(req, res, next) {
    console.log("route.get"); 
});

//最后需要將這個(gè)router掛載到應(yīng)用
app.use("/", router);

以上部分主要是整個(gè)express的中間件的掛載。總結(jié)一下:

通過(guò)app.use()掛載的中間件最終都代理到了router.use()方法下

router.use()方法,新建一個(gè)layer,layer上保存了路徑,默認(rèn)為"/",及相應(yīng)的處理方法,并存入這個(gè)app維護(hù)的stack中。

通過(guò)var router = require("express").Router()新建的router路徑級(jí)實(shí)例,同樣可以掛載不同的中間件,不過(guò)最后需要將這個(gè)router路由注入到app應(yīng)用當(dāng)中:app.use("/", router);

接下來(lái)講下當(dāng)http請(qǐng)求到來(lái)的時(shí)候,數(shù)據(jù)的流向: 在你定義中間件的過(guò)程中,因?yàn)槭蔷S護(hù)了一個(gè)app或者route實(shí)例,它們分別都有一個(gè)stack。這個(gè)stackFIFO的,因此每當(dāng)一個(gè)請(qǐng)求過(guò)來(lái)的時(shí)候,數(shù)據(jù)從最開(kāi)始的定義的中間件開(kāi)始,一直向下按順序進(jìn)行傳遞,因此你可以自己定義,當(dāng)然,你需要調(diào)用next()方法。就比如Route.protoype.dispath方法

//將req, res分發(fā)給這個(gè)route
Route.prototype.dispatch = function dispatch(req, res, done) {
  var idx = 0;
  var stack = this.stack;
  if (stack.length === 0) {
    return done();
  }

  var method = req.method.toLowerCase();
  if (method === "head" && !this.methods["head"]) {
    method = "get";
  }

  req.route = this;

  next();

  function next(err) {
    if (err && err === "route") {
      return done();
    }

    var layer = stack[idx++];
    if (!layer) {
      return done(err);
    }

    if (layer.method && layer.method !== method) {  //匹配傳入的req請(qǐng)求方式,和layer的method進(jìn)行對(duì)比
      return next(err);
    }

//調(diào)用layer.handle,用以錯(cuò)誤處理或者request處理
    if (err) {
      layer.handle_error(err, req, res, next);      
    } else {
      layer.handle_request(req, res, next);         
    }
  }
};

最后,http請(qǐng)求的處理:
app或者route實(shí)例中,自身有一個(gè)stack,這個(gè)stack就存放了在掛載中間時(shí)新建的layer,每個(gè)layer實(shí)例都保存了對(duì)應(yīng)的路徑,以及相應(yīng)的error_handlerequest_handle

謝謝大家看到這里,歡迎大家斧正。

下一篇寫寫express路由的實(shí)現(xiàn)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/80143.html

相關(guān)文章

  • [譯]Express應(yīng)用結(jié)構(gòu)最佳實(shí)踐

    摘要:為應(yīng)用增加新的特性和處理新的情況可能都會(huì)改變文件的結(jié)構(gòu)。寫一個(gè)模板的最佳實(shí)踐是,不要在模板中處理數(shù)據(jù)。在上面這四個(gè)文件夾中,主要的測(cè)試代碼將是單元測(cè)試,這意味著你需要將被測(cè)試的代碼與應(yīng)用分離開(kāi)來(lái)。 前言 Node和Express并不嚴(yán)格要求它的應(yīng)用的文件結(jié)構(gòu)。你可以以任意的結(jié)構(gòu)來(lái)組織你的web應(yīng)用。這對(duì)于小應(yīng)用來(lái)說(shuō),通常是不錯(cuò)的,十分易于學(xué)習(xí)和實(shí)驗(yàn)。 但是,當(dāng)你的應(yīng)用在體積和復(fù)雜性上都...

    dreamans 評(píng)論0 收藏0
  • Web框架常用架構(gòu)模式(JavaScript語(yǔ)言)

    摘要:只能在不同的時(shí)候選用不同的假設(shè)和不同的理論來(lái)解釋問(wèn)題,許來(lái)西的文章講到科學(xué)一定程度上通過(guò)放棄一貫性換取了實(shí)用性,放棄自洽性換取了它洽性。然而遺憾的是本身只提供了模塊和洋蔥模型的最小封裝。 在寫干貨之前,我想先探(qiang)討(diao)兩個(gè)問(wèn)題,模式的局限性?模式有什么用? 最近看到一篇文章對(duì)我啟發(fā)很大,許來(lái)西在知乎的回答《哲學(xué)和科學(xué)有什么關(guān)聯(lián)?》,全篇較長(zhǎng),這里摘錄我要引出的一點(diǎn):...

    loostudy 評(píng)論0 收藏0
  • 通過(guò)nodeclub項(xiàng)目源碼來(lái)講解如何做一個(gè)nodejs + express + mongodb項(xiàng)目

    摘要:是的源碼,算是一個(gè)基本的博客系統(tǒng),包含文章發(fā)布,關(guān)注,評(píng)論等功能。這些功能可以說(shuō)是任何一個(gè)網(wǎng)站的基礎(chǔ)。比如運(yùn)營(yíng)數(shù)據(jù)配置和其他數(shù)據(jù)配置分開(kāi),因?yàn)楹苡锌赡苄枰鲆粋€(gè)小的工具來(lái)讓非技術(shù)人員配置相關(guān)參數(shù)。模式在中有一個(gè)專門的章節(jié)來(lái)講解。 1. About 1.1 what: nodeclub是cnodejs.com的源碼,cnode算是一個(gè)基本的博客系統(tǒng),包含文章發(fā)布, 關(guān)注,評(píng)論等功能...

    kaka 評(píng)論0 收藏0
  • Express到Nestjs,談?wù)凬estjs設(shè)計(jì)思想和使用方法

    摘要:三的洋蔥模型這里簡(jiǎn)單講講在中是如何分層的,也就是說(shuō)請(qǐng)求到達(dá)服務(wù)端后如何層層處理,直到響應(yīng)請(qǐng)求并將結(jié)果返回客戶端。從而使得端支持跨域等。 ??最近已經(jīng)使用過(guò)一段時(shí)間的nestjs,讓人寫著有一種java spring的感覺(jué),nestjs可以使用express的所有中間件,此外完美的支持typescript,與數(shù)據(jù)庫(kù)關(guān)系映射typeorm配合使用可以快速的編寫一個(gè)接口網(wǎng)關(guān)。本文會(huì)介紹一下作...

    chanjarster 評(píng)論0 收藏0
  • Express到Nestjs,談?wù)凬estjs設(shè)計(jì)思想和使用方法

    摘要:三的洋蔥模型這里簡(jiǎn)單講講在中是如何分層的,也就是說(shuō)請(qǐng)求到達(dá)服務(wù)端后如何層層處理,直到響應(yīng)請(qǐng)求并將結(jié)果返回客戶端。從而使得端支持跨域等。 ??最近已經(jīng)使用過(guò)一段時(shí)間的nestjs,讓人寫著有一種java spring的感覺(jué),nestjs可以使用express的所有中間件,此外完美的支持typescript,與數(shù)據(jù)庫(kù)關(guān)系映射typeorm配合使用可以快速的編寫一個(gè)接口網(wǎng)關(guān)。本文會(huì)介紹一下作...

    wawor4827 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<