paypal之nodejs 框架 Kraken-js 源碼分析

本文是基于 kraken-js 0.6.1 版本的

關于如何使用kraken-js 可以去看看官網的使用文檔 點擊這里?。kraken-js 是基于express之上的,目的在于讓工程師更多的去關注代碼邏輯,少關注自身的開發環境,所以他將express所有的一些公用的配置都寫在了配置文件里面了。暴露給開發者的很少。下面來看看kraken-js 源碼,分析是如何創建nodejs應用程序的。如果對express 不太理解的,可以看看我之前博客寫的express框架解密序列。

按照kraken-js 官網上的步驟,我們創建了一個新項目,然后生成了代碼是這個樣子的:

對這個結構說明下:

  • /config ?---應用程序和中間件的配置
  • /controllers ?---路由和邏輯
  • /lib ---用戶開發的第三方庫
  • /locales ---語言包
  • /models ?---模型
  • /public ---公共資源
  • /public/templates ---模板
  • /tests ---一些測試用例
  • index.js ---應用程序入口

我們看看入口文件 index.js ?和路由文件controllers/index.js

'use strict';var kraken = require('kraken-js'),app = {};app.configure = function configure(nconf, next) {// Fired when an app configures itselfnext(null);
};app.requestStart = function requestStart(server) {// Fired at the beginning of an incoming request
};app.requestBeforeRoute = function requestBeforeRoute(server) {// Fired before routing occurs
};app.requestAfterRoute = function requestAfterRoute(server) {// Fired after routing occurs
};kraken.create(app).listen(function (err) {if (err) {console.error(err);}
});
'use strict';module.exports = function (server) {server.get('/', function (req, res) {var model = { name: 'helloworld' };res.render('index', model);});};

我們看到在index.js 文件里面,沒有端口設置,沒有模板引擎以及其他的中間件設置,只有一些鉤子,這個就是kraken-js 的巧妙之處。

具體做法是:

  首先創建一個app,給app添加一些方法(configure,requestStart,requestBeforeRoute,requestAfterRoute),再將app作為kraken的create方法參數

傳進去,kraken.create(app),然后在調用listen方法(主要是調用http的createServer創建一個服務)。其實這里的app是一些鉤子,作為代理的方式給express框架使用,這些方法的參數server 就是express 框架創建的實例。我們在使用的時候,都是通過express的use方法來調用中間件的。這個后面會有詳細的解釋。

既然調用了kraken 的create方法,我們就去看看kraken 的源碼,看看是怎么構造這個應用程序的。

我們看到在kraken-js/index.js 中,提供了一個對外的調用函數:

exports.create = function (route, delegate) {return create().use(route, delegate);
};

可以看到它先調用了本身的create方法,然后調用了use方法,我們去看看create方法:

function create() {return Object.create(kraken, {_app: {enumerable: true,writable: true,value: null},_promise: {enumerable: true,writable: true,value: null},host: {enumerable: true,writable: true,value: undefined},port: {enumerable: true,writable: true,value: undefined},'?': {// This is silly, but since a require-d app may be using// kraken, but the proto isn't a reference to the same// object, we need to have a unique identifier for the// `isKraken` check. (Non-enumerable.)value: '?'}});
}

它創建了一個kraken的對象,然后添加了一些屬性。在use的方法中,最重要的是:

 chain = create(this._app).then(assign).then(create).then(mount);

首先,通過調用create方法傳入了一個空的app,而這個create方法是

function create(delegate) {return appcore.create(delegate, pathutil.create(resolveRoot));}

它直接調用了appcore的create方法:

function create(delegate, resolver, callback) {var app, appcore;if (isExpress(delegate)) {callback(null, delegate);return;}if (typeof resolver === 'function') {callback = resolver;resolver = pathutil.create();}app = express();if (!delegate) {patch.apply('stream', app);callback(null, app);return;}appcore = Object.create(proto, {_app: {enumerable: true,writable: false,value: app},_delegate: {enumerable: true,writable: false,value: delegate},_resolver: {enumerable: true,writable: false,value: resolver},_config: {enumerable: true,writable: true,value: undefined},_i18n: {enumerable: true,writable: true,value: undefined}});appcore.init(function (err) {if (err) {callback(err);return;}callback(null, app);});
}

我們知道這個這個參數的app是為空的。所以進入了這個分支:

app = express();if (!delegate) {patch.apply('stream', app);callback(null, app);return;}

直接將express導入進來了。patch.apply('stream', app); 是將app的response 直接改寫了。返回了express 。接著執行:

.then(assign)
.then(create)
.then(mount);

assign 是將app賦值給this._app 了,同時做了第二件事情,將應用程序的那些鉤子返回出來,直接傳遞給create函數,我們看到create函數再次調用了,與第一次不同的是,本次的參數不為空,

而是我們在應用程序創建的那個“app”,即為我們之前所說的鉤子。最后在掛載這個應用程序。

在第二次調用create方法。其實就是通過proto 創建了一個appcore的對象:

appcore = Object.create(proto, {_app: {enumerable: true,writable: false,value: app},_delegate: {enumerable: true,writable: false,value: delegate},_resolver: {enumerable: true,writable: false,value: resolver},_config: {enumerable: true,writable: true,value: undefined},_i18n: {enumerable: true,writable: true,value: undefined}});

我們看到將這個app作為appcore的_app 屬性,將delegate 其實就是作為_delagate ?的一個屬性。這個就是index.js 傳入的那個參數app對象,接著調用了proto的init方法:

appcore.init(function (err) {if (err) {callback(err);return;}callback(null, app);});

init 是直接調用了?_configure 方法,在這個方法中,首先通過賦值app為?app ?= this._app;然后通過方法?this._config = config.create(this._resolve('.'));,讀取kraken和本應用程序config 目錄下得config文件。最后再調用?next方法。在next方法中:

function next(err) {var config, settings;if (err) {callback(err);return;}config = self._config;patch.apply('config', app, config);// XXX: Special-case resolving `express:views` until we get config protocols working.config.set('express:views', self._resolve(config.get('express:views')));config.set('express:env',  config.get('env:env'));config.set('express:port', config.port);config.set('express:host', config.host);settings = config.get('express');Object.keys(settings).forEach(function (key) {app.set(key, settings[key]);});settings = config.get('ssl');if (settings) {app.set('ssl', settings);tls.SLAB_BUFFER_SIZE = settings.slabBufferSize || tls.SLAB_BUFFER_SIZE;tls.CLIENT_RENEG_LIMIT = settings.clientRenegotiationLimit || tls.CLIENT_RENEG_LIMIT;tls.CLIENT_RENEG_WINDOW = settings.clientRenegotiationWindow || tls.CLIENT_RENEG_WINDOW;}self._views();self._middleware();callback();}

這個方法中,首先配置了一些參數。

然后再調用了處理視圖的參數_views(),接著調用了?_middleware()函數。

我們看到:

if (typeof this._delegate.configure === 'function') {this._delegate.configure(this._config.raw, next);return;}

如果configure 是一個函數,那么先調用這個,然后再調用那個next方法。在前面我們說過,this._delegate 其實就是index.js 里面創建的那個“app” 鉤子。

視圖我們省略,我們主要看看怎么調用_middleware 函數這個函數都做了些什么。還是先上源碼:

_middleware: function () {var app, delegate, config, srcRoot, staticRoot, errorPages;app = this._app;delegate = this._delegate;config = this._config.get('middleware');srcRoot = this._resolve(config.static.srcRoot);staticRoot = this._resolve(config.static.rootPath);app.use(express.favicon());app.use(kraken.compiler(srcRoot, staticRoot, this._config, this._i18n));app.use(express.static(staticRoot));app.use(kraken.logger(config.logger));if (typeof delegate.requestStart === 'function') {delegate.requestStart(app);}config.bodyParser && console.warn('The `middleware:bodyParser` configuration will not be honored in future versions. Use `middleware:json`, `middleware:urlencoded`, and `middleware.multipart`.');app.use(express.json(config.bodyParser || config.json));app.use(express.urlencoded(config.bodyParser || config.urlencoded));console.warn('Multipart body parsing will be disabled by default in future versions. To enable, use `middleware:multipart` configuration.');app.use(express.multipart(config.bodyParser || config.multipart || { limit: 2097152 })); // default to 2mb limit
app.use(express.cookieParser(config.session.secret));app.use(kraken.session(config.session));app.use(kraken.appsec(config.appsec));if (typeof delegate.requestBeforeRoute === 'function') {delegate.requestBeforeRoute(app);}enrouten(app).withRoutes({directory: this._resolve(this._config.get('routes:routePath'))});if (typeof delegate.requestAfterRoute === 'function') {delegate.requestAfterRoute(app);}errorPages = config.errorPages || {};app.use(kraken.fileNotFound(errorPages['404']));app.use(kraken.serverError(errorPages['500']));app.use(kraken.errorHandler(config.errorHandler));},

在這里我們看到了很熟悉的express 的基本配置,它的一些配置都寫在這里面了。

我們看到這些,這些就是處理我們之前的那些鉤子函數了。看到了吧,都是傳入express 構建的應用程序。

if (typeof delegate.requestStart === 'function') {delegate.requestStart(app);}if (typeof delegate.requestBeforeRoute === 'function') {delegate.requestBeforeRoute(app);}if (typeof delegate.requestAfterRoute === 'function') {delegate.requestAfterRoute(app);}

同時在這里,有

 enrouten(app).withRoutes({directory: this._resolve(this._config.get('routes:routePath'))});

這個其實是處理路由的,它使調用了express-enrouten 模塊,進入看看這個函數干了寫什么事情,可以看到

module.exports = function (app) {return {withRoutes: function (settings) {settings = settings || {};if (settings.index) {require(resolve(settings.index))(app);return;}// Directory to scan for routesloaddir(settings.directory).forEach(function (file) {var controller = require(file);if (typeof controller === 'function' && controller.length === 1) {controller(app);}});(settings.routes || []).forEach(function (def) {assert.ok(def.path, 'path is required');assert.ok(typeof def.handler === 'function', 'handler is required');var method = (def.method || 'get').toLowerCase();app[method](def.path, def.handler);});}};};

可以看到,她直接將每一個controller文件require 進來了。然后直接將他作為了一個構造函數,將app作為一個參數傳進去了。 所以在我們的controller 里面都是這種方式:

'use strict';module.exports = function (server) {server.get('/', function (req, res) {var model = { name: 'helloworld' };res.render('index', model);});};

這里的server其實就是app,看到了把,這個就是kraken ? 的妙處。

轉載于:https://www.cnblogs.com/yupeng/p/3484189.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/259036.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/259036.shtml
英文地址,請注明出處:http://en.pswp.cn/news/259036.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

go build 參數_Go語言 通過go bulid -tags 實現編譯控制

Go語言提供的build tag 條件編譯特性,顧名思義,只有在特定條件下才會構建對應的代碼。比如下面的源文件只有在設置debug構建標志時才會被構建:// build debugpackage mainvar buildMode "debug"可以用以下命令構建:go …

selinux 的管理

第十單元selinux 的管理一 顯示及更改 SELINUX 模式getenforce ###顯示selinux模式setenforce 0|1 ##0指permissive警告,1 表示 enforcing強制###vim /etc/sysconfig/selinux ###修改selinux開機狀態###注:disable表示關閉&…

ubuntu15.10下安裝opencv2.4.9python上調用opencv庫

對于centos,可以參考:Install OpenCV-Python in Fedora 如果IPP難以下載可以在cmake時禁掉它,只需:cmake -DWITH_IPPOFF OpenCV3.3CUDA9.0 安裝過程中遇到的問題,解析: https://blog.csdn.net/u014613745/a…

【轉】jquery 注冊事件的方法

原文鏈接:http://outofmemory.cn/code-snippet/2123/jquery-zhuce-event-method 1.使用事件名來綁定,可用的事件名有 change,click,dblclick,error,focus,focusin,focusout,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,…

ffmpeg 時間戳

轉http://blog.csdn.net/yfh1985sdq/article/details/5721953 AVpacket里的時間戳pts和dts.單位好像是us. 問 : 時間戳pts和dts,這里兩個時間戳各有什么意義? 答 : 顯示時間,解碼時間. DTS:decoding time stamp PTS:presentation time stamp Generally …

鍵盤改鍵軟件_一秒五鍵,一鍵三招,萬種光污染,杜伽K310櫻桃軸機械鍵盤感受...

機械鍵盤我一直用的青軸,或者各種其他名字但其實本質就是青軸的。喜歡青軸那種清脆的聲音,在我聽來如同山間小溪流水般的叮咚。不過這聲音在夜間分外的具有穿透力,更會在人身體不好的時候難以承受,所以每每用過之后卻又不得不換回…

ubuntu 15.10下cmake 的安裝

因為原先ubuntu自帶的cmake有點舊,就想著安裝個最新的,可是直接安裝卡在了某一步上,后面有說明。現將正確的安裝方法列出來。1.卸載原有的版本sudo apt-get autoremove cmake2. 下載最新的cmake :https://cmake.org/download/3. 解壓&#xf…

codeigniter鉤子的使用

CodeIgniter 的鉤子功能,使得我們可以在不修改系統核心文件的基礎上,來改變或增加系統的核心運行功能。可是鉤子究竟該怎么用呢?雖然不是很難,不過很多剛用ci的朋友可能還是不明白怎么用。 通過本文的簡單實例,大家一下…

wxWidgets之wxGrid控件

1. 介紹wxGrid控件時wxWidgets界面庫中內置的網格控件。通經常使用來顯示表格數據。該控件擁有強大的功能。開發人員可依據自己的需求對其進行定制。 2. 經常使用API 構造函數:wxGrid ()wxGrid (wxWindow *parent, wxWindowID id, const wxPoint &poswxDef…

powerdesigner畫關系圖_想畫好手繪,這些圖你一定要畫一下!

畫好手繪除了對透視要深入了解掌握以及線條運用把握之外,還有很重要的就是要對空間物體的關系、比例、光影關系都要理解透徹。大體快可分割成多個x小體塊。其實當年學習的繪畫基礎也是畫好手繪的基礎,畫手繪依然需要去理解整體畫面的空間黑白灰、物體穿插…

C#,pdf文件轉換成圖片文件。

本文采用Adobe Acrobat9.0的COM組件,將Pdf文件的每一頁轉換成對應的圖片文件。 開發環境:VS2010,.Net Framework4.0,Adobe Acrobat9.0。 工程中添加COM引用:Adobe Acrobat 9.0 Type Library(必須裝了Adobe …

Android進程間通信

一.Linux系統進程間通信有哪些方式? 1.socket; 2.name pipe命名管道; 3.message queue消息隊列; 4.singal信號量; 5.share memory共享內存; 二.Java系統的通信方式是什么? 1.socket; 2.name pip…

最新的一些開源face alignment及評價

dlib :https://github.com/davisking/dlib/tree/v18.18 評價:速度快,可商用,有些時候不太準確 2. CLM-framework: https://github.com/TadasBaltrusaitis/CLM-framework 評價:很準確,不可商用 3. Face Detection…

1048 石子歸并

1048 石子歸并 時間限制: 1 s 空間限制: 128000 KB 題目等級 : 黃金 Gold 題目描述 Description有n堆石子排成一列,每堆石子有一個重量w[i], 每次合并可以合并相鄰的兩堆石子,一次合并的代價為兩堆石子的重量和w[i]w[i1]。問安排怎樣的合并順序&#xf…

internetreadfile讀取數據長度為0_【完結】TensorFlow2.0 快速上手手冊

大家好,這是專欄《TensorFlow2.0》的第五篇文章,我們對專欄《TensorFlow2.0》進行一個總結。我們知道全新的TensorFlow2.0 Alpha已經于2019年3月被發布,新版本對TensorFLow的使用方式進行了重大改進,為了滿足各位AI人對TensorFlow…

Facial Landmark Detection(人臉特征點檢測)

原文地址:http://www.learnopencv.com/facial-landmark-detection/#comment-2471797375 作為計算機視覺研究員,我們很早就開始研究人臉。人臉分析領域最廣為人知的就是人臉識別(face recognition).但是為了識別一幅圖像中的人臉&…

cpu卡操作協議iso14443協議

http://baike.baidu.com/link?url3mef2ZMRoNuBrVLA2HpEh8xrBtzACdIi5nIDUsMyVkA8OulIXGWgswvFcTiBfh_B轉載于:https://www.cnblogs.com/shuenjian901/p/3496331.html

Python 字符串的內置函數

方法描述string.capitalize()把字符串的第一個字符大寫string.center(width)返回一個原字符串居中,并使用空格填充至長度 width 的新字符串string.count(str, beg0, endlen(string))返回 str 在 string 里面出現的次數,如果 beg 或者 end 指定則返回指定范圍內 str …

Java中的Error和Exceptiond的異同點

Error和Exception的異同點: (1)Error類和Exception類都繼承超類Java.lang.Throwable (2)Error:一般指與虛擬機相關的問題,如系統崩潰,內存溢出等。對于這類錯誤,僅靠程序…

python算法題_python基本算法題(一)

1、3位水仙花數計算 "3位水仙花數”是指一個三位整數,其各位數字的3次方和等于該數本身。 例如: ABC是一個“3位水仙花數”,則:A的3次方+B的3次方+C的3次方 ABC。 使用Python,輸出所有的3…