thinkjs/lib/Lib/Core/App.js
2014-07-03 22:17:59 +08:00

280 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var cluster = require('cluster');
var fs = require('fs');
var domain = require('domain');
var thinkHttp = thinkRequire('Http');
var Dispatcher = thinkRequire('Dispatcher');
var App = module.exports = {};
/**
* 根据http里的group和controller获取对应的controller实例
* @param {[type]} http [description]
* @return {[type]} [description]
*/
App.getBaseController = function(http){
'use strict';
var gc = ucfirst(http.group) + '/' + ucfirst(http.controller) + 'Controller';
var path = getThinkRequirePath(gc);
if (path) {
return require(path)(http);
}
}
/**
* controller不存在时调用的默认controller
* @return {[type]} [description]
*/
App.getCallController = function(http){
'use strict';
var config = C('call_controller');
if (!config) {
return;
}
if (isString(config)) {
config = config.split(':');
}
var action = Dispatcher.getAction(config.pop());
var controller = Dispatcher.getController(config.pop());
var group = Dispatcher.getGroup(config.pop());
var instance = this.getBaseController({
group: group,
controller: controller
})
if (instance && isFunction(instance[action + C('action_suffix')])) {
http.group = group;
http.controller = controller;
http.action = action;
}
return instance;
}
/**
* 执行具体的action调用前置和后置操作
* @return {[type]} [description]
*/
App.execAction = function(controller, action, data, callMethod){
'use strict';
//action操作
var act = action + C('action_suffix');
var flag = false;
//action不存在时执行魔术方法
if (callMethod && !isFunction(controller[act])) {
var call = C('call_method');
if (call && isFunction(controller[call])) {
flag = true;
act = call;
}
}
//action不存在
if (!isFunction(controller[act])) {
return getPromise('action `' + action + '` not found. ', true);
}
var promise = getPromise();
//action前置操作
var before = C('before_action');
if (before && isFunction(controller[before])) {
promise = controller[before](action);
}
promise = promise.then(function(){
//action魔术方法只传递action参数
if (flag) {
return controller[act](action);
}
if (data) {
return controller[act].apply(controller, data);
}else{
return controller[act]();
}
});
//action后置操作
var after = C('after_action');
if (after && isFunction(controller[after])) {
promise = promise.then(function(){
return controller[after](action);
})
}
return promise;
}
/**
* 获取action的形参
* @return {[type]} [description]
*/
App.getActionParams = function(fn, http){
'use strict';
//注释的正则
var commentReg = /((\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s))/mg;
//获取形参的正则
var parsReg = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var toString = fn.toString().replace(commentReg, '');
var match = toString.match(parsReg)[1].split(/\s*,\s*/);
//匹配到形参
var params;
if (match && match.length) {
params = match.map(function(item){
return http.post[item] || http.get[item] || '';
});
}
return params;
}
/**
* 执行
* @param {[type]} http [description]
* @return {[type]} [description]
*/
App.exec = function(http){
'use strict';
var controller = this.getBaseController(http) || this.getCallController(http);
//controller不存在
if (!controller) {
var err = new Error('Controller `' + http.controller + '` not found. ' + http.pathname);
return getPromise(err, true);
}
var params;
var actionFn = controller[http.action + C('action_suffix')];
//参数绑定
if (C('url_params_bind') && isFunction(actionFn)) {
params = this.getActionParams(actionFn, http);
}
var promise = getPromise(controller.__initReturn);
var self = this;
return promise.then(function(){
return self.execAction(controller, http.action, params, true);
})
}
/**
* 发送错误信息
* @param {[type]} error [description]
* @return {[type]} [description]
*/
App.sendError = function(http, error){
'use strict';
var message = isError(error) ? error.stack : error;
console.log(message);
if (!http.res) {
return;
}
if (APP_DEBUG) {
http.res.statusCode = 500;
http.res.end(message);
}else{
http.res.statusCode = 500;
http.setHeader('Content-Type', 'text/html; charset=' + C('encoding'));
var readStream = fs.createReadStream(C('error_tpl_path'));
readStream.pipe(http.res);
readStream.on('end', function(){
http.res.end();
});
}
}
/**
* run
* @return {[type]} [description]
*/
App.run = function(){
'use strict';
if (APP_MODE && App.mode[APP_MODE]) {
return App.mode[APP_MODE]();
}
return App.mode.http();
};
/**
* 不同模式下的run
* @type {Object}
*/
App.mode = {
//命令行模式
cli: function(){
'use strict';
var defaultHttp = thinkHttp.getDefaultHttp(process.argv[2]);
thinkHttp(defaultHttp.req, defaultHttp.res).run().then(App.listener);
},
//HTTP模式
http: function(){
'use strict';
var clusterNums = C('use_cluster');
//不使用cluster
if (!clusterNums) {
return App.createServer();
}
//使用cpu的个数
if (clusterNums === true) {
clusterNums = require('os').cpus().length;
}
if (cluster.isMaster) {
for (var i = 0; i < clusterNums; i++) {
cluster.fork();
}
cluster.on('exit', function(worker) {
console.log('worker ' + worker.process.pid + ' died');
process.nextTick(function(){
cluster.fork();
});
});
}else {
App.createServer();
}
}
};
/**
* 创建服务
* @return {[type]} [description]
*/
App.createServer = function(){
'use strict';
//自定义创建server
var createServerFn = C('create_server_fn');
if (createServerFn) {
if (isFunction(createServerFn)) {
return createServerFn(App);
}else if (isFunction(global[createServerFn])) {
return global[createServerFn](App);
}
}
var server = require('http').createServer(function (req, res) {
thinkHttp(req, res).run().then(App.listener);
});
thinkRequire('WebSocket')(server, App).run();
server.listen(C('port'));
}
/**
* 监听回调函数
* @param {[type]} http [description]
* @return {[type]} [description]
*/
App.listener = function(http){
'use strict';
//自动发送thinkjs和版本的header
http.setHeader('X-Powered-By', 'thinkjs-' + THINK_VERSION);
//禁止远程直接用带端口的访问,websocket下允许
if (C('use_proxy') && http.host !== http.hostname && !http.websocket) {
http.res.statusCode = 403;
http.res.end();
return getDefer().promise;
}
var domainInstance = domain.create();
var deferred = getDefer();
domainInstance.on('error', function(err){
App.sendError(http, err);
deferred.reject(err);
});
domainInstance.run(function(){
return tag('app_init', http).then(function(){
return Dispatcher(http).run();
}).then(function(){
return tag('app_begin', http);
}).then(function(){
return tag('action_init', http);
}).then(function(){
return App.exec(http);
}).then(function(){
return tag('app_end', http);
}).catch(function(err){
App.sendError(http, err);
}).then(function(){
deferred.resolve();
})
});
return deferred.promise;
};