你是否为项目规范而烦恼?是否因为不同项目环境需要引入不同配置而发愁?是否反复定义和注册各种路由?别担心,我有一套解决方案——ElpisCore,它能帮助你快速启动项目,专注于核心逻辑的开发。
ElpisCore 的核心理念
ElpisCore 的主要职责是处理一些与业务无关的基础配置,使得开发人员可以更加专注于业务逻辑的实现。它将项目文件细分为七个维度,分别是:
- config(配置项)
- middleware(中间件)
- router(路由)
- router-schema(路由配置)
- service(服务)
- controller(控制器)
- extend(扩展)
这样一来,项目结构清晰、模块化,使得开发工作更加高效。
ElpisCore 基于 Koa
ElpisCore 是基于 Koa 构建的, const app = new Koa() 中的 app 对象贯穿整个项目,它是框架的核心。我们约定,项目中的 /config 目录存放配置信息,包含不同环境下的配置文件(如 config.default.js、config.local.js、config.beta.js 和 config.prod.js)。这些配置文件将通过 module.exports = {}; 方式进行自定义,并在框架启动时合并,最终挂载到 app 对象中。
项目目录结构
/config:存放各种配置文件。/app:存放业务相关文件,包含中间件、路由、服务等。/app/middleware:存放中间件。/app/router:存放路由定义。/app/router-schema`:存放路由参数校验规范。/app/service:存放服务文件。/app/controller:存放控制器。/app/extend:存放扩展功能(如日志工具)。
中间件
在 ElpisCore 中,所有中间件文件都会被挂载到 app.middlewares 对象上,而不需要手动引入每个中间件文件。我们只需在 /app/middleware 目录下按规定格式书写中间件即可。
module.exports = (app) => {
// 返回一个中间件
return async (ctx, next) => {
// 逻辑处理
};
};
路由定义与注册
ElpisCore 自动处理路由的注册。我们只需要在 /app/router 目录下定义路由文件,并按照一定格式进行书写,框架会自动加载和注册路由。
module.exports = (app, router) => {
const { view: viewController } = app.controller;
// 用户输入 http://ip:port/view/xxx 能渲染出对应的页面
router.get("/view/:page", viewController.renderPage.bind(viewController));
};
ElpisCore 将自动注册路由,而不需要手动引入并注册每个路由文件。
路由参数校验
大部分 API 都需要进行参数校验,ElpisCore 将校验规则放置在 /app/router-schema 目录下,采用 JSON Schema 规范书写。校验规则按 [path].[method] 的方式进行配置。
module.exports = {
"/api/project/list": {
get: {
query: {
type: "object",
properties: {
proj_key: { type: "string" },
},
required: ["proj_key"],
},
},
},
};
在 /app/middleware 中定义接口参数校验中间件 api-params-verify.js,该中间件将自动验证请求的参数。
const Ajv = require("ajv");
const ajv = new Ajv();
module.exports = (app) => {
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;
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;
let validate;
// 校验 headers, body, query 和 params
if (valid && headers && schema.headers) {
schema.headers.$schema = "http://json-schema.org/draft-07/schema";
validate = ajv.compile(schema.headers);
valid = validate(headers);
}
if (valid && body && schema.body) {
schema.body.$schema = "http://json-schema.org/draft-07/schema";
validate = ajv.compile(schema.body);
valid = validate(body);
}
if (valid && query && schema.query) {
schema.query.$schema = "http://json-schema.org/draft-07/schema";
validate = ajv.compile(schema.query);
valid = validate(query);
}
if (valid && params && schema.params) {
schema.params.$schema = "http://json-schema.org/draft-07/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;
}
return await next();
};
};
服务文件
服务文件存放在 /app/service 目录,定义与业务逻辑相关的服务。每个服务通过继承 BaseService 来实现,方便进行复用。
module.exports = (app) => {
const BaseService = require("./base")(app);
return class ProjectService extends BaseService {
async getList() {
return [
{ name: "project1", desc: "project1 desc" },
{ name: "project2", desc: "project2 desc" },
{ name: "project3", desc: "project3 desc" },
];
}
};
};
ElpisCore 会自动实例化服务,并将其挂载到 app.service 对象中,无需手动引入每个服务文件。
控制器
控制器文件存放在 /app/controller 目录,负责接收请求并返回响应。控制器中的方法与服务进行交互,返回处理结果。
module.exports = (app) => {
const BaseController = require("./base")(app);
return class ProjectController extends BaseController {
async getList(ctx) {
const { project: projectService } = app.service;
const projectList = await projectService.getList();
this.success(ctx, projectList);
}
};
};
ElpisCore 会自动实例化控制器,并将其挂载到 app.controller 对象中,无需手动引入每个控制器文件。
扩展功能
扩展功能文件存放在 /app/extend 目录,用于提供一些额外的工具或功能,如日志记录。ElpisCore 会自动加载扩展,并将其挂载到 app 对象中。
const log4js = require("log4js");
module.exports = (app) => {
let logger;
if (app.env.isLocal()) {
logger = console;
} else {
log4js.configure({
appenders: {
console: { type: "console" },
dateFile: {
type: "dateFile",
filename: "./logs/application.log",
pattern: ".yyyy-MM-dd",
},
},
categories: {
default: { appenders: ["console", "dateFile"], level: "trace" },
},
});
logger = log4js.getLogger();
}
return logger;
};
总结
ElpisCore 是一套成熟的企业级框架,通过分离配置项、路由、服务、控制器等,使得开发者能够专注于业务逻辑的开发。它通过自动化的方式处理路由注册、中间件管理、参数校验等,极大地简化了开发流程,提高了开发效率。无论是配置、路由、服务还是扩展,ElpisCore 都提供了清晰的结构和优雅的实现,帮助开发者快速、高效地构建和维护项目。