Expressミドルウェアの設計



Express Middleware Design



まだ役に立たないexpress書かれたserver最初に、ソースコードのいくつかをもう一度叩きます。

expressエントリファイルlibフォルダの下express.jsいくつかのメソッドを外の世界に公開しました。



最も重要なexpress.js 36〜47行目):

function createApplication() { var app = function(req, res, next) { App.handle(req, res, next) //Processing entry for each middleware, the handle method is extended to proto by mixin } 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

私たちはしばしばこれを私たち自身のビジネスコードで書きます:



var express = require('express') var app = express()

実際、それはcreateApplicationメソッドと呼ばれています。これにより、アプリがインスタンス化されます。これはappより特別なmixin他のいくつかのプロパティを統合する

Mixin(app, EventEmitter.prototype, false) //Expanded the event emitter prototype object Mixin(app, proto, false) //Expanded properties and methods in application.js

ビジネスコードでインスタンス化しますapp呼び出されますapp.init()メソッドはいくつかの初期構成を完了します。init()メソッドもfrom application.js継承されます。

エントリファイルは非常に明確で、主に完了メソッドとappいくつかの初期化操作の公開です。



次の外観application.js次のコードロジックの一部:

行136-146、a _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行目、これはそれぞれmiddleware入口、

app.handle = function handle(req, res, callback) { Var router = this._router //Get the router that has been instantiated // 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) //When http comes over, the processing for request and response starts from this place }

行187-242、app.useメソッドはアプリケーションレベルを提供しますmiddlewareしかし実際には行214で、this.lazyrouter()新しいルートを作成し、行219-221、そしてapp.use(fn)パラメータ渡されたものはroute.use()ルーティングレベルのミドルウェアがオンになっています。app.use()はいroute.useエージェント。

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)) // flatten the arguments if (fns.length === 0) { throw new TypeError('app.use() requires middleware functions') } // setup router This.lazyrouter() //create a new one if there is no route instance var router = this._router fns.forEach(function (fn) { // non-express app //if the incoming app is not an express instance app if (!fn || !fn.handle || !fn.set) { Return router.use(path, fn) //Inject the middleware into the 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、エージェントto routerインスタンスroute()メソッド内:

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

routerインスタンスが渡されますrouter/index.jsこのフォルダーに42〜60行目を作成するコンストラクターを提供します。

var proto = module.exports = function(options) { var opts = options || {} function router(req, res, next) { Router.handle(req, res, next) //handle method inherits from 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 = [] //Initialize a stack. This stack holds all registered middleware. return router }

提供されたrouterコンストラクター、そのプロトタイプオブジェクトで、行136が提供されますproto.handleメソッド、このメソッドの効果は、http of req with | _ + _から受け取ることです。 |。

413行目、提供resメソッド、前述のとおりproto.useはいapp.useエージェント、このメソッドの特殊性はいずれかにありますroute.useリクエストは通過しますhttpたとえば、ミドルウェアがマウントされましたapp.use自分から多くのミドルウェアを削除したので、もう一度渡す必要があります。express4.xインストールして、ビジネスコードで参照してください。たとえばnpmミドルウェア:

body-parser

毎回var express = require('express') var app = express() var bodyParser = require('body-parser') app.use(bodyParser.json())リクエストが来ると、最初に渡されます。http方向を提供するこのミドルウェアbodyParser.json()追加reqメソッドと次の役割を渡しますミドルウェア。
同時にreq.body = {}内部、439〜458行目、

route.use

477行目for (var i = 0 i このメソッドは、新しいルートを作成する方法を提供します。

proto.route

proto.route = function route(path) { Var route = new Route(path) //Create a new route. The internal implementation of this Route constructor is shown in ./route.js, which provides an empty stack for Var layer = new Layer(path, { // create a new layer, the role of layer see the following explanation sensitive: this.caseSensitive, strict: this.strict, end: true }, route.dispatch.bind(route)) layer.route = route This.stack.push(layer) //Create a new route, this route will maintain its own stack return route }ですが、この方法は異なります
それは自分自身を維持するということですvar route = require('express').Router()、これはstackこのメソッドで上記で定義したすべてのミドルウェアがあります。繰り返しますが、これを渡すことができますstackさまざまなパスのマウントroutereq処理。

使用方法:

res

上記の部分は主に全体ですvar express = require('express') var app = express() var router = express.Router() / / No middleware that mounts any path, the middleware will be executed for each request through the route router.use(function(req, res, next) { console.log('route.use') }) router.get('/test', function(req, res, next) { console.log('route.get') }) / / Finally need to mount this router to the application app.use('/', router)ミドルウェアがマウントされています。結論として:

  1. by expressマウントされたミドルウェアが最終的に表されます。app.use()メソッド

  2. router.use()メソッド、新しいパスを作成router.use()layerパスは保存され、デフォルトはlayerおよび対応する処理メソッドであり、これに格納されます'/'維持app in。

  3. by stack新しく構築されたvar router = require('express').Router()パスレベルのインスタンスでは、さまざまなミドルウェアをマウントすることもできますが、これを最後に配置する必要があります。routerルートインジェクションrouterアプリケーション:app

次に、それについて話しましょうapp.use('/', router)リクエストが到着すると、データの流れ:ミドルウェアを定義する過程で、ミドルウェアは維持されるためhttpまたはappインスタンスには、それぞれにroute。これはstackはいstackしたがって、リクエストが着信するたびに、データは最初に定義されたミドルウェアから始まり、順番に渡されるため、自分で定義できます。もちろん、next()メソッドを呼び出す必要があります。ちょうどFIFOメソッドのように

Route.protoype.dispath

最後に、/ / Req, res distributed to this 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) { //match the incoming req request method, compared with the layer method return next(err) } / / Call layer.handle for error handling or request processing if (err) { layer.handle_error(err, req, res, next) } else { layer.handle_request(req, res, next) } } }リクエストの処理:
in httpまたはappこの例では、route、これはstackマウントの中央に新しいものを格納しましたstackそれぞれlayerインスタンスは対応するパスを保存し、対応するlayer with error_handleを保存します。

これを見てくれてありがとう、みんなが斧になることを歓迎します。

次の執筆request_handleルートの実装。