alaska
Version:
Componentized and pluggable web framework for Node.js
246 lines (209 loc) • 7.21 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _koaCompose = require('koa-compose');
var _koaCompose2 = _interopRequireDefault(_koaCompose);
var _alaska = require('../alaska');
var _utils = require('../utils');
var utils = _interopRequireWildcard(_utils);
var _api = require('../api');
var defaultApiController = _interopRequireWildcard(_api);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = async function loadApi() {
this.loadApi = utils.resolved;
for (let sub of this.serviceList) {
await sub.loadApi();
}
if (this.getConfig('prefix') === false) return;
this.debug('loadApi');
const service = this;
const { router } = service;
let serviceModules = this.alaska.modules.services[this.id];
this._apiControllers = serviceModules.api || {};
const apis = this._apiControllers;
// plugins
_lodash2.default.forEach(serviceModules.plugins, plugin => {
_lodash2.default.forEach(plugin.api, (patch, group) => {
if (!apis[group]) {
apis[group] = {};
}
_lodash2.default.forEach(patch, (fn, name) => {
if (name[0] === '_' || typeof fn !== 'function') return;
if (!apis[group][name]) {
apis[group][name] = fn;
} else if (typeof apis[group][name] === 'function') {
apis[group][name] = [fn, apis[group][name]];
} else if (Array.isArray(apis[group][name])) {
apis[group][name].unshift(fn);
}
});
});
});
//将某些API的多个中间件转换成一个
_lodash2.default.forEach(apis, api => {
_lodash2.default.forEach(api, (fn, key) => {
if (Array.isArray(fn) && key[0] !== '_') {
api[key] = (0, _koaCompose2.default)(fn);
}
});
});
const models = _lodash2.default.reduce(this.models, (res, Model) => {
res[Model.id] = Model;
return res;
}, {});
if (!_lodash2.default.size(apis) && !_lodash2.default.size(models)) return;
function onError(ctx, error) {
if (ctx.status === 404) {
ctx.status = 500;
}
//普通业务逻辑错误
if (error instanceof _alaska.NormalError) {
if (!ctx.body) {
let body = {
error: ctx.t(error.message),
code: error.code
};
if (error.code) {
if (error.code >= 200 && error.code < 600) {
//HTTP status
ctx.status = error.code;
}
}
ctx.body = body;
}
return;
}
//如果不是普通错误,则输出错误信息
console.error(`${ctx.method} ${ctx.path} -> ${error.message} \n${error.stack}`);
//如果是数据验证错误
if (error.message.startsWith('Validation failed') && error.errors) {
for (let key in error.errors) {
ctx.body = {
error: ctx.t('Validation failed.') + ' ' + error.errors[key].message,
code: 500
};
return;
}
}
ctx.body = {
error: ctx.t('Internal Server Error'),
code: 500
};
}
// API错误处理
router.all('/api/*', async (ctx, next) => {
try {
await next();
} catch (error) {
onError(ctx, error);
}
if (!ctx.body) {
if (ctx.status === 404) {
ctx.body = { error: 'Not found' };
ctx.status = 404;
}
if (ctx.status === 401) {
ctx.body = { error: 'Unauthorized' };
ctx.status = 401;
}
if (ctx.status === 403) {
ctx.body = { error: 'Forbidden' };
ctx.status = 403;
}
}
});
// 所有支持的REST接口函数名
const REST_ACTIONS = ['count', 'show', 'list', 'paginate', 'create', 'remove', 'removeMulti', 'update', 'updateMulti'];
// 记录下当前Service需要支持哪些REST API
let restApis = {};
_lodash2.default.forEach(this.models, model => {
if (!model.api) return;
REST_ACTIONS.forEach(action => {
restApis[action] = restApis[action] || !!model.api[action];
});
});
_lodash2.default.forEach(apis, api => {
REST_ACTIONS.forEach(action => {
restApis[action] = restApis[action] || !!api[action];
});
});
// 注册某个类型API
function restApi(action) {
return function (ctx, next) {
let modelId = ctx.params.model;
let Model = models[modelId];
if (!Model) {
//404
return next();
}
ctx.state.Model = Model;
let middlewares = [];
if (ctx.params.id) {
ctx.state.id = ctx.params.id;
}
// api 目录下定义的中间件
if (apis[modelId] && apis[modelId][action]) {
middlewares.push(apis[modelId][action]);
}
// Model.api参数定义的中间件
if (Model.api && Model.api[action]) {
middlewares.push(defaultApiController[action]);
}
//console.log(middlewares);
if (!middlewares.length) {
//404
return next();
}
return (0, _koaCompose2.default)(middlewares)(ctx, next);
};
}
// 判断是否存在扩展接口
let extension = !!_lodash2.default.find(apis, api => _lodash2.default.find(api, (fn, key) => key[0] !== '_' && REST_ACTIONS.indexOf(key) < 0));
if (extension) {
router.register('/api/:controller?/:action?', ['POST', 'GET'], function (ctx, next) {
let { controller, action } = ctx.params;
if (!action) {
action = 'default';
}
// $Flow
let ctrl = service._apiControllers[controller];
if (ctrl && ctrl[action] && action[0] !== '_') {
try {
if (REST_ACTIONS.indexOf(action) > -1) {
return next();
}
let promise = ctrl[action](ctx, next);
//异步函数
if (promise && promise.then) {
return promise.catch(error => {
onError(ctx, error);
});
}
//同步函数,直接返回
} catch (error) {
onError(ctx, error);
}
return null;
}
return next();
});
}
//Restful接口
if (restApis.count) router.get('/api/:model/count', restApi('count'));
if (restApis.paginate) router.get('/api/:model/paginate', restApi('paginate'));
if (restApis.show) router.get('/api/:model/:id', restApi('show'));
if (restApis.list) router.get('/api/:model', restApi('list'));
if (restApis.create) router.post('/api/:model', restApi('create'));
if (restApis.update) router.patch('/api/:model/:id', restApi('update'));
if (restApis.updateMulti) router.patch('/api/:model', restApi('updateMulti'));
if (restApis.remove) router.del('/api/:model/:id', restApi('remove'));
if (restApis.removeMulti) router.del('/api/:model', restApi('removeMulti'));
};
/* eslint no-console:0 */
/* eslint prefer-arrow-callback:0 */
/* eslint no-restricted-syntax:0 */
/* eslint guard-for-in:0 */
;