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.mzph.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…

键盘改键软件_一秒五键,一键三招,万种光污染,杜伽K310樱桃轴机械键盘感受...

机械键盘我一直用的青轴,或者各种其他名字但其实本质就是青轴的。喜欢青轴那种清脆的声音,在我听来如同山间小溪流水般的叮咚。不过这声音在夜间分外的具有穿透力,更会在人身体不好的时候难以承受,所以每每用过之后却又不得不换回…

codeigniter钩子的使用

CodeIgniter 的钩子功能,使得我们可以在不修改系统核心文件的基础上,来改变或增加系统的核心运行功能。可是钩子究竟该怎么用呢?虽然不是很难,不过很多刚用ci的朋友可能还是不明白怎么用。 通过本文的简单实例,大家一下…

powerdesigner画关系图_想画好手绘,这些图你一定要画一下!

画好手绘除了对透视要深入了解掌握以及线条运用把握之外,还有很重要的就是要对空间物体的关系、比例、光影关系都要理解透彻。大体快可分割成多个x小体块。其实当年学习的绘画基础也是画好手绘的基础,画手绘依然需要去理解整体画面的空间黑白灰、物体穿插…

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).但是为了识别一幅图像中的人脸&…

Java中的Error和Exceptiond的异同点

Error和Exception的异同点: (1)Error类和Exception类都继承超类Java.lang.Throwable (2)Error:一般指与虚拟机相关的问题,如系统崩溃,内存溢出等。对于这类错误,仅靠程序…

superviseddescent (SDM C++11实现)环境配置

今天试着用了一下SDM的C11实现,本来以为挺简单的,可是配置环境还是花了一些时间。为了给自己留下一些记忆,特把配置过程记录下来。 这个实现是C11的版本,是一个通用版本,里面包含了很多的功能,比如函数的最…

python图形小游戏代码_手把手制作Python小游戏:俄罗斯方块(一)

手把手制作Python小游戏:俄罗斯方块1大家好,新手第一次写文章,请多多指教 A.准备工作: 这里我们运用的是Pygame库,因为Python没有内置,所以需要下载 如果没有pygame,可以到官网下载 pygame官网&…

关于Git使用的一些心得

2019独角兽企业重金招聘Python工程师标准>>> 本篇稍微记录下Git使用的一些心得。 对Git的使用,应该是从搭建自己的博客开始的。当时看到开源中国推荐的一篇基于码云hexo搭建自己博客的文章。所以就花了一天时间鼓捣了下博客。 顺带整理下目前能看到我写的…

Dlib机器学习库安装

昨天使用了一下dlib的人脸检测功能,效果出奇的好。下面给出dlib整个的安装过程和使用指导。 下载安装 我们可以从dlib的官网下载最新的版本,我的是dlib18.18.然后我们需要使用cmake编译dlib库和examples示例。 当然前提是你要按照好cmake和opencv。 …

ipv4地址是几位二进制数_几张思维导图,让你清楚的知道ip地址怎么回事?

网络工程中,ip地址是必须要了解的内容,今天我们用几张思维导图来给大家详细讲解IP地址。一、什么是IP地址在生活中我们使用具有上网功能的电子设备都有IP地址,就跟每个人都有自己的名字一样。IP地址分为IPV4 IPV6,我们所说的的IP地…

《关系营销2.0——社交网络时代的营销之道》一检查拼写和语法

本节书摘来异步社区《关系营销2.0——社交网络时代的营销之道》一书中的第2章,作者: 【美】Mari Smith 译者: 张猛 , 于宏 , 赵俐 责编: 陈冀康, 更多章节内容可以访问云栖社区“异步社区”公众号查看。 检查拼写和语法 关系营销2…

dlib人脸检测功能介绍

本文主要介绍三个点: 1. 如何单独建立一个工程,使用dlib的人脸检测功能。 2. 提高人脸检测率的两个方法 3. 加速人脸检测的方法 下面围绕这几个点展开叙述。 建人脸检测工程 1 . 首先我们先使用上期说的examples里的人脸检测。 我们只要将face_de…

ios网络开发 网络状态检查

http://www.cnblogs.com/hanjun/archive/2012/12/01/2797622.html 网络连接中用到的类: 一.Reachability 1.添加 Reachability 的.h和.m文件,再添加SystemConfiguration.framework。 2.Reachability中定义了三种网络状态: typedef Num{ NotR…

《众妙之门——网页排版设计制胜秘诀》——3.4 展现品牌视觉的同时保持网页的可读性...

本节书摘来自异步社区《众妙之门——网页排版设计制胜秘诀》一书中的第3章,第3.4节,作者: 【德】Smashing Magazine 译者: 侯景艳 , 范辰 更多章节内容可以访问云栖社区“异步社区”公众号查看。 3.4 展现品牌视觉的同时保持网页的…

vs2013创建及使用DLL

这几天看了许多关于生成Dll的博文,很有感触,遂整理在此,以供自己后续参考。 VS2013创建DLL 我们使用vs2013来生成Dll,其实使用其他的版本也是同理如此。步骤如下: 单击“新建项目”,选择“Win32 项目”&a…

iOS UISegmentedControl 的使用

当用户输入不仅仅是布尔值时,可使用分段控件(UISegmentedControl)。分段控件提供一栏按钮(有时称为按钮栏),但只能激活其中一个按钮。分段控件会导致用户在屏幕上看到的内容发生变化。它们常用于在不同类别…