Koa2 开发模板,意在复用,可快速搭建一个web服务。
.
├── app # 服务相关业务代码 MVC设计模式
├── bin # 部署相关文件
├── config # 环境配置
├── public # 静态资源,如js,css,image等
├── test # 单元测试
├── utils # 工具类函数
├── app.js # 入口文件
└── package.jsonnpm run dev # 开发环境
npm start # 正式环境
npm run test # 运行单元测试
npm run lint # 代码风格检测
npm run fix # 修复有误代码风格注册koa2应用,引入相关中间件,路由,启动http服务
app文件夹主要用来放置业务相关的代码,采用MVC设计模式目录如下
app
├── controller # 控制层
├── middleware # app中间件
├── model # 模型层 数据库相关模型
├── router # 路由
├── service # 服务层
└── view # 视图层controller 主要负责校验,转发(重定向),或者渲染模板
module.exports.get = function (ctx, next) {
// 检验参数
const schema = {
userId: Joi.string().required().error(new ApiError('必须用户Id'))
}
const options = Joi.validate(ctx.params, schema)
// 调用服务
const result = detail.get(options)
// 返回结果
ctx.body = result
}
module.exports.get = async (ctx, next) => {
// 渲染 view/index/index.pug
await ctx.render('index/index', {
title: 'Koa 2 Template!'
})
}joi提供了强大的校验参数功能
根据业务编写中间件,例如:封装上下文,校验token,封装response,输出请求日志
数据库模型,用的比较多是 MySQL 和 MongoDB, npm上有相关包提供,功能完善且稳定。
MySQL 推荐 sequelize
MongoDB 推荐 mongoose
koa-router 配置http请求的路由
// router/user.js
// 路由前缀
router.prefix(`${prefix}/users`)
// 相关路由对应相关 controller
router.get('/:userId', user.get)
router.get('/', user.list)
router.post('/', user.post)
// app.js 引入路由
const routers = fs.readdirSync('./app/router')
routers.forEach(function (fileName, index) {
let router = require(`./app/router/${fileName}`)
app.use(router.routes(), router.allowedMethods())
})实现主要业务,以及与数据库交互
视图层主要放置html相关模板
koa-views 引用Consolidate支持多种模板引擎
这里使用pug(原称jade) | 中文文档
// app.js
const views = require('koa-views')
app.use(views(`${__dirname}/app/view`, {
extension: 'pug'
}))也可以根据实际情况改用其他模板不过需要安装相关渲染引擎包
封装一个错误类,中间件可区别开业务错误以及系统错误,做出不同输出。
// ApiError.js
class ApiError extends Error {
constructor (message, code = -1) {
super(message)
this.code = isNaN(message) ? code : message
}
}
module.exports = ApiError
// app/middleware/responseFormat.js:22
// 业务级别错误 info 系统级别错误 error
const errType = err instanceof ApiError ? 'info' : 'error'使用 tracer 进行日志输出 根据环境进行不同输出,本地环境在控制面板输出,生产环境会写入以日期命名的log文件,方便出现bug时可查阅日志。
具体实现,查看 /utils/Logger.js
log中间件,输出请求参数以及请求结果,以便追踪请求
// app/middleware/log.js
const logger = require('../../utils/Logger')
const log = async (ctx, next) => {
ctx.logger = ctx.logger || logger
ctx.logger.info('receive', ctx.url, ctx.params)
await next()
ctx.logger.info('response', ctx.body)
}
module.exports = log经过中间件封装后,可直接从上下文(ctx)调用logger
相关service集成 base类后 可直接调用this.logger
封装请求结果有两个目的 1.统一请求结果数据结构 2.捕捉错误,统一封装错误信息数据结构
系统运行难免会遇到各种错误,业务级别或者系统级别的,这些错误 若不统一数据结构,前端不好操作。为此,捕捉错误封装结果并且根据级别输出日志。
详细代码可看 app/middleware/responseFormat.js
用法简单,功能强大。不需要编写多个if去判断请求参数。
详细文档 joi
将校验函数(validate)再封装一层,使得校验失败时,抛出业务错误,以便中间件捕获,放回结果到前端。
const Joi = require('joi')
const validate = Joi.validate
Joi.validate = (value, schema, options = {
convert: true, // 尝试安所需转换类型
abortEarly: false, // 发现第一个错误是否停止继续检验
stripUnknown: true, // 是否删除未定义属性
allowUnknown: false // 是否允许包含未定义属性
}) => {
const validateResult = validate.call(Joi, value, schema, options)
if (validateResult && validateResult.error) {
throw validateResult.error
}
// 返回处理过的参数
return validateResult.value
}
module.exports = Joi1.utils/Constant.js
2.config
Constant是业务常量,放置与业务相关的常量量。例如:错误信息,请求前缀,常用列表等。
config是环境常量,放置不同环境下同名的常量。例如:数据库连接,应用环境,各种id/secret/key等。可参考config文件夹
- 日期处理函数 moment
- 微信公众号相关 co-wechat-api
- 微信网页授权 co-wechat-oauth
- redis客户端 ioredis
- 定时任务 node-schedule
- JobScheduler(任务队列) Agenda
积累项目经验,持续完善ing.....