Expressソース学習-ルーティング編
13463 ワード
Expressの概要
Expressは簡潔で柔軟なnode.js Webアプリケーションフレームワークであり、さまざまなWebアプリケーションを作成し、豊富なHTTPツールを提供します.Expressを使用すると、完全な機能を備えたWebサイトを迅速に構築できます.
Express公式サイトもとてもフレンドリーなドキュメントです.
Expressフレームワークのコア特性:ルーティング:異なるHTTP要求動作を実行するためのルーティングテーブルが定義される. ミドルウェア:HTTP要求に応答するためにミドルウェアを設定できます. テンプレートエンジン:テンプレートにパラメータを渡すことでHTMLページを動的にレンダリングできます.
ここではまずExpressルーティングから解析を学習し,その他のコア特性の学習と探索を継続する.
Expressのインストール(V 4.16.2)
Expressの簡単な使用
1.getリクエストを受信するサーバの作成
2.上記コードの実行
Nodemonは、ソースの変更を自動的に再起動し、サーバの監視を自動的に再開するユーティリティです.
上記のテストケースに基づいてexpressを実装
ソース分析
単純ルーティングの登録
expressソースディレクトリ構造
ルーティングシステム
ルーティングミドルウェアでは、Routerルーティングシステム全体にstackが1つずつlayerを格納し、layer.routeを介してrouteルーティングオブジェクトを指し、routeのstackの中にも1つのlayerが格納され、各layerには(method/handler)が含まれる.
ソースコードには主にいくつかのクラスと方法が含まれています. express()はapp を返します.
実際にexpressは内部createApplication関数を指してappを返す
アプリケーション上の多くのプロパティメソッドはアプリケーション.jsからエクスポートされたprotoオブジェクトから来ています.router/index.jsにもprotoという名前の関数オブジェクトに静的プロパティメソッドproto.handle proto.param proto.useがマウントされています.
//アプリケーション.jsがエクスポートしたprotoに、appにマージされたリクエストメソッドをマウントします.ソースコードは次のとおりです.
この.lazyrouterは、Routerインスタンスを作成してapp._にマウントするために使用します.router
登録ミドルウェア
2つの方法でミドルウェアを追加できます:app.useは非ルーティングミドルウェアを追加するために使用され、app[method]はルーティングミドルウェアを追加します.この2つの追加方法は、Routerの関連メソッドを内部で呼び出して実現されます.
非ルーティングミドルウェアの登録
app.useの下部でrouter.useが呼び出されていることがわかりました.次にrouter.useを見てみましょう.
ルーティングミドルウェアの登録
上のアプリケーション.jsのappオブジェクトにはhttpがたくさん追加されています.要求app[method]はルーティングミドルウェアを登録するために使用されます.
上のapp[method]でrouter[method]つまりthis.router.route[method]
これを見てみましょうrouter.route(path); このコードに何が起こったの?
routerのthis.router.routeはrouteオブジェクトを作成し、layerがpathとroute.dispatchをlayerに渡し、layerのrouteがrouteオブジェクトを指してRouteとRouteを関連付け、最後にrouteオブジェクトを戻り値とする
ルーティングミドルウェアの場合、ルーティングコンテナのstack(Router.stack)のlayerはrouteフィールドを介してルーティングオブジェクトを指します.これにより、Router.stackはRoute.stackと関連付けられ、関連付けられた概略モデルは次の図に示されます.
app[method]でroute[method].apply(route,slice.call(arguments,1))を呼び出します.
最後にrouter/route.jsのRouteを見てみましょう
Routeでのallメソッド
最終ルーティング登録関係チェーンapp[method]=>router[method]=>route[method]最終的にroute[method]でルーティング登録を完了
次にLayerを見てみましょう
サーバの起動
ルーティングコール
app.handle呼び出しthis.router.handleルーティング処理
express.jsのapp.handle実際にアプリケーション.jsのapp.handle最下位からrouter.handleを呼び出す
router.handle呼び出し内部next関数routerのstackで一致するlayerを探す
ルーティング処理app.handle=>router.handle=>layer.handle_request
layerのhandle_request呼び出しnext一次取得route stackでの処理方法
小結
app.useは非ルーティングミドルウェアを追加するために使用され、app[method]はルーティングミドルウェアを追加し、ミドルウェアの追加はRouterとRouteで完了する必要があり、appはfacadeに相当し、追加の詳細を包装した.
Routerはミドルウェアを保管した容器と見なすことができる.中に格納されているルーティングミドルウェアの場合、Router.stackのlayerにはroute属性が対応するルーティングオブジェクトを指しているため、Router.stackをRoute.stackに関連付け、Routerを介してルーティングオブジェクトの各プロセッサに遍歴することができます.
Expressは簡潔で柔軟なnode.js Webアプリケーションフレームワークであり、さまざまなWebアプリケーションを作成し、豊富なHTTPツールを提供します.Expressを使用すると、完全な機能を備えたWebサイトを迅速に構築できます.
Express公式サイトもとてもフレンドリーなドキュメントです.
Expressフレームワークのコア特性:
ここではまずExpressルーティングから解析を学習し,その他のコア特性の学習と探索を継続する.
Expressのインストール(V 4.16.2)
npm i express -S
Expressの簡単な使用
1.getリクエストを受信するサーバの作成
// 1.get.js
const express = require('express');
const app = express();
const port = 8080;
// path ,callback 。
app.get('/', function(req,res){ // get
res.end('hello express!'); //
});
app.listen(port,function(){ //
console.log(`server started on port ${port}`);
});
2.上記コードの実行
Nodemonは、ソースの変更を自動的に再起動し、サーバの監視を自動的に再開するユーティリティです.
$ nodemon 1.get.js
localhost:8080 :
hello express!
localhost:8080/user :
Cannot GET /user 404 Not Found
上記のテストケースに基づいてexpressを実装
ソース分析
単純ルーティングの登録
app.get('/get', function(req, res) {
res.send('hello express');
});
expressソースディレクトリ構造
ルーティングシステム
ルーティングミドルウェアでは、Routerルーティングシステム全体にstackが1つずつlayerを格納し、layer.routeを介してrouteルーティングオブジェクトを指し、routeのstackの中にも1つのlayerが格納され、各layerには(method/handler)が含まれる.
ソースコードには主にいくつかのクラスと方法が含まれています.
createApplicaton
Application(proto)
Router
Route
Layer
実際にexpressは内部createApplication関数を指してappを返す
var mixin = require('merge-descriptors'); // merge-descriptors ,
var proto = require('./application'); // application.js proto
function createApplicaton() { // express()
var app = function(req, res, next) { // app
app.handle(req, res, next); //
};
mixin(app, EventEmitter.prototype, false); // EventEmitter.prototype app
mixin(app, proto, false); // proto app.get app.post app
app.init(); //
return app;
}
アプリケーション上の多くのプロパティメソッドはアプリケーション.jsからエクスポートされたprotoオブジェクトから来ています.router/index.jsにもprotoという名前の関数オブジェクトに静的プロパティメソッドproto.handle proto.param proto.useがマウントされています.
// router/index.js
var proto = module.exports = function(options) {}
//アプリケーション.jsがエクスポートしたprotoに、appにマージされたリクエストメソッドをマウントします.ソースコードは次のとおりです.
// application.js
// methods , http app , 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);
}
this.lazyrouter(); // Router this._router = new Router();
var route = this._router.route(path); // router/index proto.route
route[method].apply(route, slice.call(arguments, 1)); //
return this;
};
});
この.lazyrouterは、Routerインスタンスを作成してapp._にマウントするために使用します.router
// application.js
methods.forEach(function(method){
app[method] = function(path){
this.lazyrouter(); // Router this._router = new 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));
}
};
登録ミドルウェア
2つの方法でミドルウェアを追加できます:app.useは非ルーティングミドルウェアを追加するために使用され、app[method]はルーティングミドルウェアを追加します.この2つの追加方法は、Routerの関連メソッドを内部で呼び出して実現されます.
非ルーティングミドルウェアの登録
// application.js
app.use = function(fn) { // fn [fn]
var offset = 0;
var path = '/';
var fns = flatten(slice.call(arguments, offset)); //
// setup router
this.lazyrouter(); //
var router = this._router;
fns.forEach(function(fn) {
router.use(path, function mounted_app(req, res, next) { // app.use router.use
var orig = req.app;
fn.handle(req, res, function (err) {
setPrototypeOf(req, orig.request)
setPrototypeOf(res, orig.response)
next(err);
});
});
}
app.useの下部でrouter.useが呼び出されていることがわかりました.次にrouter.useを見てみましょう.
// router/index.js
var Layer = require('./layer');
proto.use = function(fn) {
var offset = 0; //
var path = '/'; // / use
// path
if (typeof arg !== 'function') {
offset = 1;
path = fn;
}
// [fn, fn] slice [[fn, fn]]
var callbacks = flatten(slice.call(arguments, offset)); //
for(var i = 0; i < callbacks.length; i++) { //
var fn = callbacks[i];
if (typeof fn !== 'function') { //
throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn))
}
//
// layer
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: false,
end: false
}, fn);
// , undefined
layer.route = undefined;
this.stack.push(layer); // layer router.stack
}
}
ルーティングミドルウェアの登録
上のアプリケーション.jsのappオブジェクトにはhttpがたくさん追加されています.要求app[method]はルーティングミドルウェアを登録するために使用されます.
// application.js
var app = exports = module.exports = {};
var Router = require('./router');
var methods = require('methods');
methods.forEach(function(method){
app[method] = function(path){ // app app.get app.post app.put
if (method === 'get' && arguments.length === 1) {
// app.get(setting)
return this.set(path);
}
this.lazyrouter(); // Router this._router
// this._router.route => router/index.js proto.route
var route = this._router.route(path);
route[method].apply(route, slice.call(arguments, 1)); // router method
return this;
};
});
上のapp[method]でrouter[method]つまりthis.router.route[method]
これを見てみましょうrouter.route(path); このコードに何が起こったの?
var route = this._router.route(path); // route
route[method].apply(route, slice.call(arguments, 1)); // route route[method]
routerのthis.router.routeはrouteオブジェクトを作成し、layerがpathとroute.dispatchをlayerに渡し、layerのrouteがrouteオブジェクトを指してRouteとRouteを関連付け、最後にrouteオブジェクトを戻り値とする
// router/index.js
var Route = require('./route');
var Layer = require('./layer');
proto.route = function (path) {
var route = new Route(path); // app[method] route
var layer = new Layer(path, { // route layer
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, route.dispatch.bind(route)); // route dispatch layer
// ( ), Router Route
layer.route = route;
this.stack.push(layer); // layer Router stack
return route; // route
}
ルーティングミドルウェアの場合、ルーティングコンテナのstack(Router.stack)のlayerはrouteフィールドを介してルーティングオブジェクトを指します.これにより、Router.stackはRoute.stackと関連付けられ、関連付けられた概略モデルは次の図に示されます.
app[method]でroute[method].apply(route,slice.call(arguments,1))を呼び出します.
// router/index.js
// application.js app[method] router http all
methods.concat('all').forEach(function(method){
proto[method] = function(path){
var route = this.route(path) // route
route[method].apply(route, slice.call(arguments, 1)); // router[method] route[method]
return this;
};
});
最後にrouter/route.jsのRouteを見てみましょう
// router/route.js
function Route(path) { // Route
this.path = path;
this.stack = []; // route stack
debug('new %o', path)
this.methods = {}; // HTTP
}
var Layer = require('./layer');
var methods = require('methods');
// Route http
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);
}
debug('%s %o', method, this.path)
var layer = Layer('/', {}, handle); // route layer method handle
layer.method = method;
this.methods[method] = true; // method
this.stack.push(layer); // layer route stack
}
return this; // route
};
});
Routeでのallメソッド
Route.prototype.all = function all() {
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.all() requires a callback function but got a ' + type
throw new TypeError(msg);
}
var layer = Layer('/', {}, handle);
layer.method = undefined; // all
this.methods._all = true; // all
this.stack.push(layer); // route stack
}
return this;
};
最終ルーティング登録関係チェーンapp[method]=>router[method]=>route[method]最終的にroute[method]でルーティング登録を完了
次にLayerを見てみましょう
// route/layer.js
var pathRegexp = require('path-to-regexp');
module.exports = Layer;
function Layer(path, options, fn) {
if (!(this instanceof Layer)) {
return new Layer(path, options, fn);
}
this.handle = fn; //
this.regexp = pathRegexp(path, this.keys = [], opts); //
}
Layer.prototype.match = function(path) { // this.regexp
}
サーバの起動
// application.js
var http = require('http');
app.listen = function listen() {
var server = http.createServer(this); // this => express.js app
return server.listen.apply(server, arguments);
};
// express.js app
var app = function(req, res, next) {
app.handle(req, res, next);
};
ルーティングコール
app.handle呼び出しthis.router.handleルーティング処理
express.js
function createApplicaton() {
let app = function(req, res, next) { //
app.handle(req, res, next); //
}
}
express.jsのapp.handle実際にアプリケーション.jsのapp.handle最下位からrouter.handleを呼び出す
// application.js
app.handle = function handle(req, res, callback) {
var router = this._router;
// final handler
var done = callback || finalhandler(req, res, {
env: this.get('env'),
onerror: logerror.bind(this)
});
router.handle(req, res, done); // router.handle
};
router.handle呼び出し内部next関数routerのstackで一致するlayerを探す
// router/index.js
function matchLayer(layer, path) {
try {
return layer.match(path);
} catch (err) {
return err;
}
}
proto.handle = function(req, res, done) {
// middleware and routes
var stack = self.stack; // router stack
var idx = 0;
next();
function next(err) {
if (idx >= stack.length) { //
setImmediate(done, layerError);
return;
}
//
var path = getPathname(req);
var layer;
var match;
var route;
while (match !== true && idx < stack.length) { //
layer = stack[idx++]; // router stack layer
match = matchLayer(layer, path); // matchLayer layer match
route = layer.route; // route
var method = req.method; //
var has_method = route._handles_method(method); // route _handles_method Boolean
if (match !== true) { //
return done(layerError);
}
if (route) { // layer handle_request handle
return layer.handle_request(req, res, next);
}
}
}
}
ルーティング処理app.handle=>router.handle=>layer.handle_request
layerのhandle_request呼び出しnext一次取得route stackでの処理方法
Layer.prototype.handle_request = function handle(req, res, next) {
var fn = this.handle; // new Layer handle
// function Layer() {
// this.handle = fn;
// }
if (fn.length > 3) {
// not a standard request handler
return next();
}
try {
fn(req, res, next);
} catch (err) {
next(err);
}
};
小結
app.useは非ルーティングミドルウェアを追加するために使用され、app[method]はルーティングミドルウェアを追加し、ミドルウェアの追加はRouterとRouteで完了する必要があり、appはfacadeに相当し、追加の詳細を包装した.
Routerはミドルウェアを保管した容器と見なすことができる.中に格納されているルーティングミドルウェアの場合、Router.stackのlayerにはroute属性が対応するルーティングオブジェクトを指しているため、Router.stackをRoute.stackに関連付け、Routerを介してルーティングオブジェクトの各プロセッサに遍歴することができます.