Elpis 介绍
Elpis 是一个基于 Koa.js 的轻量级 Node.js Web 框架,提供了一套优雅的应用程序结构和加载机制。 它不是一个“大而全”的框架,而是一个带有约定目录、自动加载、路由注册、参数校验、模板渲染能力的小型应用骨架。
一、项目整体定位
主要内容:
- 基于
Koa搭建应用运行时 - 通过约定目录自动加载
config / service / controller / middleware / router / router-schema / extend - 提供统一的全局中间件机制
- 支持页面渲染
ssr和 API 接口 - 为 API 增加签名校验、参数校验能力
项目大致目录结构如下
elpis/
├── elpis-core/ # 核心框架层
│ ├── index.js # 框架启动入口
│ ├── env.js # 环境判断
│ └── loader/ # 加载器集合
│ ├── config.js # 配置加载器
│ ├── controller.js # 控制器加载器
│ ├── service.js # 服务层加载器
│ ├── middleware.js # 中间件加载器
│ ├── router.js # 路由加载器
│ ├── router-schema.js # 路由参数校验 Schema 验证加载器
│ └── extend.js # 扩展加载器
├── app/ # 应用层
│ ├── controller/ # 控制器层
│ ├── service/ # 服务层
│ ├── middleware/ # 中间件层
│ ├── router/ # 路由层
│ ├── router-schema/ # 路由参数验证
│ ├── extend/ # 框架扩展
│ └── middleware.js # 全局中间件配置
└── config/ # 配置层
├── config.default.js # 默认配置
├── config.local.js # 本地环境配置
├── config.beta.js # 测试环境配置
└── config.prod.js # 生产环境配置
二、启动流程
项目整体流程图如下:
- 要理解
KOA的洋葱圈模型,请求从外层中间件进入,一层层向内传递,到达核心后再一层层向外返回。 - 采用分层设计:
Controller: 接收请求params / body / query调用Service,返回响应,controller基类统一收拢controller相关的公共方法success / fail等Service: 业务层,service基类统一收拢service相关的公共方法Router: 路由层,负责把 URL 映射到 Controller 项目入口核心在elpis-core/index.js。
启动时主要做了几件事:
- 创建 Koa 实例
- 挂载基础信息到
app - 初始化环境变量对象
app.env - 按顺序加载各类资源
- 注册全局中间件
- 注册路由
- 启动 HTTP 服务
目前的加载顺序大致是:
config -> service -> middleware -> global middleware -> routerSchema -> controller -> extend -> router
这个顺序本质上是依赖顺序:
config是最基础的,很多模块初始化时会直接用 app.configservice先于 controller,因为 controller 往往依赖 servicemiddleware先加载定义,再在app/middleware.js中统一注册routerSchema要在请求真正进来前准备好,供参数校验中间件使用controller先于 router ,因为路由注册时要绑定 controller 方法router初始化的时候需要依赖 middleware, routerSchema, controller,因此放在最后
三、自动加载机制
Elpis 的一个核心特性是“自动加载”。
目前项目已经实现了这些 loader:
config loaderservice loadermiddleware loadercontroller loaderrouter-schema loaderextend loaderrouter loader
它们的共同思路基本一致:
- 使用
glob扫描约定目录下的文件 - 将文件名转换成对象属性名
- 挂载到
app对象上
使用时如下:
app.service.xxxapp.controller.xxxapp.middlewares.xxxapp.routerSchema.xxx
这让业务代码的组织方式更统一,也避免了每个模块里反复手动 require,用户只需要按照约定编写代码。
四、页面渲染能力
项目支持服务端模板渲染SSR - server side rendering,关键在于引入了 koa-nunjucks-2。
在全局中间件中注册后,ctx 上就会拥有 render() 方法,代码如下:
// app/middleware.js
...
// 模版引擎渲染
const koaNunjucks = require('koa-nunjucks-2');
app.use(koaNunjucks({
ext: 'tpl',
path: path.resolve(process.cwd(), './app/public'),
nunjucksConfig: {
noCache: true,
trimBlocks: true
}
}));
...
// controller 中
await ctx.render('output/entry.xxx', data)
目前页面路由例如:/view/:page,访问后会根据动态参数渲染对应模板,比如:/view/page1
在controller会进一步使用:ctx.params.page,拿到page1定位到对应模板app\public\output\entry.page1.tpl进行渲染。
五、API 接口处理能力
项目当前已经具备一条比较清晰的 API 请求处理链:
- 请求进入 Koa
- 经过全局中间件
- 执行签名校验
- 执行参数校验
- 进入
router.routes() - 匹配到 controller
- 调用 service
- 返回响应
六、基于 JSON Schema 的接口参数校验
项目目前已经接入了 Ajv,通过 router-schema 目录维护接口的参数规则。
例如一个接口可以声明:
headersquerybodyparams
对应的参数校验中间件会根据请求内容逐项校验。
七、签名校验机制
在参数校验之前,项目还做了 API 签名校验。
目前逻辑比较简单:
- 从请求头中读取时间戳和签名
- 使用约定的
signKey生成签名 - 判断签名是否一致
- 判断请求是否超时
这部分实现虽然基础,但已经具备了接口防随意调用和基本防重放的雏形。
八、日志系统
在extend/logger.js中实现,采用log4js
区分本地和线上环境,本地直接使用console,线上环境使用log4js把日志输出并落地到磁盘(日志落盘)
九、总结
从目前阶段来看,Elpis 已经不是一个简单的 Koa demo 了,而是一个具备框架能力的项目骨架。
完成的核心功能如下:
- 自动加载机制 - 7个核心加载器
- 分层架构设计 - Controller/Service
- 环境管理系统 - 支持多环境部署
- 配置管理系统 - 分环境配置覆盖
- 扩展机制 - 灵活的框架扩展能力
- 异常处理 - 全局错误捕获和处理
- 参数验证 - 基于JSON Schema的API验证
- 日志系统 - 分环境的日志处理
架构优势:
- 约定优于配置,减少开发配置工作
- 分层清晰,职责分离,易于维护
- 自动化程度高,提升开发效率
- 扩展性强,支持插件化开发
- 企业级特性,支持多环境部署