引言
在当今的软件开发领域,服务端框架的选择至关重要,它直接影响着项目的开发效率、可维护性和性能。elpis-core
作为一个服务端框架,具备丰富的功能和灵活的架构,能够帮助开发者快速搭建稳定的服务端应用。本文将深入探讨 elpis-core
的架构设计、核心功能以及实际应用中的一些要点。
整体架构设计
elpis-core
采用模块化设计,各个模块职责清晰,相互协作,共同构建了一个完整的服务端框架。主要模块包括环境配置、中间件加载、路由配置、控制器和服务层等。
环境配置(env.js)
环境配置模块负责根据不同的环境(本地、测试、生产)提供相应的配置信息。通过 process.env._ENV
变量来判断当前环境,提供了 isLocal
、isBeta
、isProduction
和 get
等方法。
javascript
module.exports = (app) => {
return {
//判断是否本地环境
isLocal() {
return process.env._ENV === 'local';
},
//判断是否测试环境
isBeta() {
return process.env._ENV === 'beta';
},
//判断是否生成环境
isProduction() {
return process.env._ENV === 'production';
},
//获取当前环境
get() {
return process.env._ENV ?? 'local';
}
}
}
这种设计使得项目在不同环境下能够灵活配置,提高了代码的可移植性和可维护性。
模块加载器
elpis-core
提供了多个模块加载器,用于加载配置、中间件、路由、控制器和服务等。这些加载器使用 glob
模块来查找相应的文件,并将其挂载到 app
对象上。
配置加载器(config.js)
配置加载器根据当前环境加载不同的配置文件,将默认配置和环境配置合并到 app.config
中。
javascript
module.exports = (app) => {
//找到 config/ 目录
const configPath = path.resolve(app.baseDir, `.${sep}config`);
//获取 default.config
let defaultConfig = {};
try {
defaultConfig = require(path.resolve(configPath, `.${sep}config.default.js`))
} catch (error) {
console.error('[exception] there is no default.config file');
}
//获取 env.config
let envConfig = {};
try {
if (app.env.isLocal()) { //本地环境
envConfig = require(path.resolve(configPath, `.${sep}config.local.js`))
} else if (app.env.isBeta()) { //测试环境
envConfig = require(path.resolve(configPath, `.${sep}config.beta.js`))
} else if (app.env.isProduction()) { //生产环境
envConfig = require(path.resolve(configPath, `.${sep}config.prod.js`))
}
} catch (error) {
console.error('[exception] there is no env.config file');
}
//覆盖并加载 config 配置
app.config = Object.assign({}, defaultConfig, envConfig)
}
中间件加载器(middleware.js
)
中间件加载器负责加载所有的中间件,并将其挂载到 app.middlewares
对象上。
ini
module.exports = (app) => {
//读取 app/middleware/**/**.js 下所有的文件
const middlewarePath = path.resolve(app.businessPath, `.${sep}middleware`);
const fileList = glob.sync(path.resolve(middlewarePath, `.${sep}**${sep}**.js`));
//遍历所有文件目录,把内容加载到 app.middlewares 下
const middlewares = {};
fileList.forEach(file => {
//提取文件名
let name = path.resolve(file);
//截取路径 app/middleware/custom-module/custom-middleware.js => custom-module/custom-middleware
name = name.substring(name.lastIndexOf(`middleware${sep}`) + `middleware${sep}`.length, name.lastIndexOf('.'));
//把 '-' 统一改为驼峰式,custom-module/custom-middleware.js => customModule/customMiddleware.js
name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());
// 挂载middleware 到内存app对象中
let tempMiddleware = middlewares;
const names = name.split(sep);
for (let i = 0, len = names.length; i < len; ++i) {
if (i === len - 1) {
tempMiddleware[names[i]] = require(path.resolve(file))(app);
} else {
if (!tempMiddleware[names[i]]) {
tempMiddleware[names[i]] = {};
}
tempMiddleware = tempMiddleware[names[i]];
}
}
});
app.middlewares = middlewares;
}
路由加载器(router.js)
路由加载器解析所有 app/router/
下的路由文件,并将其注册到 KoaRouter
中。
javascript
module.exports = (app) => {
//找到路由文件路径
const routerPath = path.resolve(app.businessPath, `.${sep}router`);
//实例化 KoaRouter
const router = new KoaRouter();
//注册所有路由
const fileList = glob.sync(path.resolve(routerPath, `.${sep}**${sep}**.js`));
fileList.forEach(file => {
require(path.resolve(file))(app, router);
})
//路由兜底(健壮性)
router.get('*', async (ctx, next) => {
ctx.status = 302; //临时重定向
ctx.redirect(`${app?.options?.homePage ?? '/' }`);
})
//路由注册到app上
app.use(router.routes());
app.use(router.allowedMethods());
}
控制器和服务层
elpis-core
支持控制器和服务层的分离,控制器负责处理请求和响应,服务层负责处理业务逻辑。
控制器(controller
)
以 ProjectController
为例,它继承自 BaseController
,提供了获取项目列表的方法。
javascript
module.exports = (app) => {
const BaseController = require("./base")(app);
return class ProjectController extends BaseController {
/**
* 获取项目列表
* @param {object} ctx
*/
async getList(ctx) {
const { proj_key: projKey } = ctx.request.query;
console.log(projKey);
const { project: projectService } = app.service;
const projectList = await projectService.getList();
this.success(ctx, projectList);
}
};
};
服务层(service
)
ProjectService
继承自 BaseService
,实现了获取项目列表的具体业务逻辑。
javascript
module.exports = (app)=>{
const BaseService = require('./base')(app);
return class ProjectService extends BaseService {
async getList(){
return [{
name:'阿萨德',
dic:'2'
},{
name:'sad',
dic:'23'
}]
}
}
}
核心功能
中间件机制
elpis-core
提供了丰富的中间件,包括静态文件服务、模板渲染、请求体解析、异常处理、API 签名校验和参数校验等。
异常处理中间件(error-handler.js)
异常处理中间件负责捕获所有异常,并提供统一的错误处理和响应。
ini
module.exports = (app) => {
return async (ctx, next) => {
try {
await next();
} catch (error) {
//异常处理
const { status, message, detail } = error;
app.logger.info(JSON.stringify(error));
app.logger.info("[-- exception --]:", error);
app.logger.info("[-- exception --]:", status, message, detail);
if(message && message.indexOf('template not found') > -1){
//重定向页面
ctx.status = 302; //临时重定向
ctx.redirect(`${app.options?.homePage}`);
return;
}
const resBody = {
success:false,
code:50000,
message:'网络异常 请稍后重试'
}
ctx.status = 200;
ctx.body = resBody;
}
};
};
API 参数校验中间件(api-params-verify.js)
API 参数校验中间件使用 ajv
库对请求参数进行校验,确保请求的合法性。
ini
const Ajv = require("ajv");
const ajv = new Ajv();
module.exports = (app) => {
const $schema = "http://json-schema.org/draft-07/schema#";
return async (ctx, next) => {
//只对 API 请求做签名校验
if (ctx.path.indexOf("/api") < 0) {
return await next();
}
//获取请求参数
const { body, query, headers } = ctx.request;
const { params, path, method } = ctx;
console.log(params)
app.logger.info(`[${method} ${path}] body: ${JSON.stringify(body)}`);
app.logger.info(`[${method} ${path}] query: ${JSON.stringify(query)}`);
app.logger.info(`[${method} ${path}] params: ${JSON.stringify(params)}`);
app.logger.info(`[${method} ${path}] headers: ${JSON.stringify(headers)}`);
const schema = app.routerSchema[path]?.[method.toLowerCase()];
if (!schema) {
return await next();
}
let valid = true;
//ajv校验器
let validate;
//校验 headers
if (valid && headers && schema.headers) {
schema.headers.$schema = $schema;
validate = ajv.compile(schema.headers);
valid = validate(headers);
}
//校验 body
if (valid && body && schema.body) {
schema.body.$schema = $schema;
validate = ajv.compile(schema.body);
valid = validate(body);
}
//校验 query
if (valid && query && schema.query) {
schema.query.$schema = $schema;
validate = ajv.compile(schema.query);
valid = validate(query);
}
//校验 params
if (valid && params && schema.params) {
schema.params.$schema = $schema;
validate = ajv.compile(schema.params);
valid = validate(params);
}
if (!valid) {
ctx.status = 200;
ctx.body = {
success: false,
message: `request validate fail: ${ajv.errorsText(validate.errors)}`,
code: 442,
};
return;
}
await next();
};
};
路由配置
elpis-core
通过路由加载器自动加载 app/router/
下的路由文件,支持动态路由和路由兜底。
javascript
module.exports = (app, router) => {
const { view: viewController } = app.controller;
//用户输入 http://ip:port/view/xxxx 能渲染出对应的页面
router.get("/view/:page", viewController.renderPage.bind(viewController));
};
模板渲染
elpis-core
使用 koa-nunjucks-2
作为模板渲染引擎,支持模板文件的渲染。
php
const koaNunjucks = require("koa-nunjucks-2");
app.use(
koaNunjucks({
ext: "tpl",
path: path.resolve(process.cwd(), "./app/public"),
nunjucksConfig: {
noCache: true,
trimBlocks: true,
},
})
);
实际应用要点
项目启动
在 index.js 中调用 ElpisCore.start
方法启动项目,并传入配置选项。
php
const ElpisCore = require("./elpis-core");
//启动项目
ElpisCore.start({
name: "Elpis",
homePage: "/",
});
环境配置
根据不同的环境(本地、测试、生产),在 config
目录下创建相应的配置文件,确保项目在不同环境下的配置正确。
总结
elpis-core
是一个功能强大、架构灵活的服务端框架,通过模块化设计和丰富的功能模块,能够帮助开发者快速搭建稳定的服务端应用。其环境配置、中间件机制、路由配置和模板渲染等功能,为项目的开发和维护提供了便利。在实际应用中,开发者可以根据项目需求进行定制和扩展,充分发挥 elpis-core
的优势。
抖音"哲玄前端"《大前端全栈实践》