引言
课程学习来自于“哲玄前端”,哲玄大佬的讲解深入浅出,令我收益匪浅。实在是良心的课程,在实践中学到了很多编程思想和设计思路。
1. 项目概述
Elpis 项目是一个基于 Koa 框架构建的服务端应用,它整合了多种功能模块,包括路由管理、中间件处理、服务调用和配置管理等。支持不同环境(本地、测试、生产)的配置,能满足多样化的开发和部署需求。
2. 项目结构
.eslintignore
.eslintrc.json
.git/
.gitignore
README.md
app/
middleware.js
controller/
base.js
project.js
view.js
extend/
logger.js
middleware/
api-params-verify.js
api-sign-verify.js
error-handler.js
public/
static/
router/
project.js
view.js
router-schema/
project.js
service/
base.js
project.js
config/
config.beta.js
config.default.js
config.local.js
config.prod.js
elpio-core/
env.js
index.js
loader/
config.js
controller.js
extend.js
middleware.js
router-schema.js
router.js
service.js
index.js
package.json
关键目录及功能
- app:存放业务逻辑代码,包含控制器、中间件、服务、扩展等。
- controller:控制器层:处理HTTP请求
- extend:框架扩展点
- middleware:中间件:请求预处理。
- router:路由定义:URL映射
- router-schema:API参数验证规则
- service:服务层:封装业务逻辑
- elpio-core:框架核心代码
- index:框架启动入口
- env:环境变量处理
- loader:模块加载器
核心功能实现
1. 洋葱模型中间件系统
基于Koa的洋葱模型,设计了一套灵活的中间件系统。每个请求都会经过一系列中间件处理,形成一个完整的处理管道。(如下图)
module.exports = (app) =>{
return async (ctx, next) =>{
try {
await next()
} catch(err) {
// 异常处理
const {status, message, detail } = err
app.logger.info(JSON.stringify(err))
app.logger.error('[-- exception -- ]', err)
app.logger.error('[-- 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
}
}
}
2.参数校验
基于AJV的强大参数校验系统,自动验证API请求的各部分(headers、query、body、params):
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
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 = {
suceess: false,
message: `request validate fail: ${ajv.errorsText(validate.errors)}`,
code: 445
}
return
}
await next()
}
}
3.服务端渲染
内置Nunjucks模板引擎支持,实现高效的服务端渲染:
module.exports = (app)=>{
return class ViewController{
/**
* 渲染页面
* @param {object} ctx 上下文
*/
async renderPage(ctx) {
await ctx.render(`dist/entry.${ctx.params.page}`, {
name: app.options?.name,
env: app.env.get(),
options: JSON.stringify(app.options)
})
}
}
}
4.模块加载化
框架通过加载系统,自动加载各类业务信息
配置加载的优化
module.exports = {
/**
* 启动项
* @param {*} options 项目配置
* options = {
* name // 项目名称
* homePath // 项目首页
* }
*/
start(options = {}) {
// koa实例
const app = new Koa()
// 应用配置
app.options = options
// console.log(app.options);
// 基础路径
app.baseDir = process.cwd();
// console.log(app.baseDir);
// 业务文件路径
app.businessPath = path.resolve(app.baseDir, `.${sep}app`)
// console.log( app.businessPath);
// 初始化环境配置
app.env = env()
console.log(`-- [start] env: ${app.env.get()} `);
// 加载 middleware
middlewareLoader(app)
console.log(`-- [start] lod middleware done`);
// 加载 routerSchema
routerSchemaLoader(app)
console.log(`-- [start] lod routerSchema done`);
// 加载 controller
controllerLoader(app)
console.log(`-- [start] lod controller done`);
// 加载 service
serviceLoader(app)
console.log(`-- [start] lod service done`);
// 加载 config
configLoader(app)
console.log(`-- [start] lod config done`);
// 加载 extend
extendLoader(app)
console.log(`-- [start] lod extend done`);
// 注册全局中间件
try {
require(`${app.businessPath}${sep}middleware.js`)(app)
console.log(`-- [start] lod global middleware done -- `);
} catch{
console.log('[exception] there is no global middleware file');
}
// 加载 router
routerLoader(app)
console.log(`-- [start] lod router done`);
// 启动服务器
try{
const port = process.env.PORT || 8080;
const host = process.env.IP || '0.0.0.0';
app.listen(port,host);
console.log(`Server runnin on port: ${port}`)
} catch(e){
console.error(e);
}
}
}
总结
Elpis Core 是一个设计良好的轻量级服务引擎框架,其模块化设计和清晰的架构使其非常适合构建中小型服务。虽然存在一些限制,但整体而言是一个值得学习和使用的框架。它的架构设计思想对于学习 Node.js 服务端框架设计很有帮助。
对于想要学习服务端框架设计的开发者来说,Elpis Core 提供了很好的学习范例,特别是在模块化设计、服务架构设计等方面。