搭建一个Koa后端项目脚手架的方法步骤(2)

'use strict' const Koa = require('koa') const bodyParser = require('koa-bodyparser')() const staticCache = require('koa-static-cache') const config = require('./config') const publicRouter = require('./routes/public') const privateRouter = require('./routes/private') const { loggerMiddleware } = require('./middlewares/logger') const { errorHandler, responseHandler } = require('./middlewares/response') const app = new Koa() // Logger app.use(loggerMiddleware) // Error Handler app.use(errorHandler) // Global Middlewares app.use(bodyParser) app.use(staticCache(config.publicDir)) // Routes app.use(publicRouter.routes(), publicRouter.allowedMethods()) app.use(privateRouter.routes(), privateRouter.allowedMethods()) // Response app.use(responseHandler) module.exports = app

这个文件中,我们可以看到较多的中间件,中间件的执行顺序是从外到内,再从内到外,也就是洋葱模式。如果还不大了解中间的小伙伴可以去查找相关资料。中间件的执行过程是依靠 app.use() 进行传递的,你可以简单的理解为自己编写的函数,依次去执行即可。每一个中间件会在app调用是传入2个参数,分别为: ctx 和 next

ctx:

Koa Context 将 node 的 request 和 response 对象封装在一个单独的对象里面,其为编写 web 应用和 API 提供了很多有用的方法。

这些操作在HTTP服务器开发中经常使用,因此其被添加在上下文这一层,而不是更高层框架中,因此将迫使中间件需要重新实现这些常用方法。

next:

下一个中间件函数,也就是每一个中间件如果要往下走必须写上这个,否则无法执行。

可以理解为前端的vue-Router中的路由守卫中的next(), 执行下一步或者进行传参。

middlewares

在这个项目主要用到了几个中间件,一个是 logger.js 、 response.js 和 jwt.js 等其他中间件。

logger.js

大家可以想一下,如果我们项目在开发中,或者上线了,我们要看我们执行的日志或者请求的参数以及报错等信息,如果没有再每一个请求中体现出来,那么遇到问题我们会很难定位到是前端的问题还是后端。而logger这个中间件就是用来对这些情况进行处理的,原来的koa模板中,只是简单的进行log的打印而已,这个中间件是用了log4js模块进行封装的。详细使用方法查看官方文档,这个中间件会在控制台或者日志中打印出固定的格式,http请求方法、返回状态、请求url、IP地址、请求时间等,而且我们也可以很好的利用log4js中的配置,来打印出自定义的日志。可以代替 console.log() 使用,在使用这个中间件的时候,必须放在第一个中间件,才能保证所以的请求及操作会先经过logger进行记录再到下一个中间件。其代码如下:

'use strict' const fs = require('fs') const path = require('path') const log4js = require('log4js') const config = require('../config') const logsDir = path.parse(config.logPath).dir if (!fs.existsSync(logsDir)) { fs.mkdirSync(logsDir) } log4js.configure({ appenders: { console: { type: 'console' }, dateFile: { type: 'dateFile', filename: config.logPath, pattern: '-yyyy-MM-dd' } }, categories: { default: { appenders: ['console', 'dateFile'], level: 'info' } } }) const logger = log4js.getLogger('[Default]') const loggerMiddleware = async (ctx, next) => { const start = new Date() await next() const ms = new Date() - start const remoteAddress = ctx.headers['x-forwarded-for'] || ctx.ip || ctx.ips || (ctx.socket && (ctx.socket.remoteAddress || (ctx.socket.socket && ctx.socket.socket.remoteAddress))) let logText = `${ctx.method} ${ctx.status} ${ctx.url} 请求参数: ${JSON.stringify(ctx.request.body)} 响应参数: ${JSON.stringify(ctx.body)} - ${remoteAddress} - ${ms}ms` logger.info(logText) } module.exports = { logger, loggerMiddleware }

response.js

这个中间件主要是用来对返回前端的响应进行处理,基础的koa模板中,我们可以用 ctx.body 进行返回前端,但是发现有些东西经常重复写,还不如提出来进行封装,而且还不用担心返回的格式会不一致。 先看看代码:

'use strict' const { logger } = require('./logger') // 这个middleware用于将ctx.result中的内容最终回传给客户端 // 回传的格式遵循这样的格式:{ code: 0, msg: any data: any } const responseHandler = (ctx) => { if (ctx.result !== undefined) { ctx.type = 'json' ctx.body = { code: 200, msg: ctx.msg || '', data: ctx.result } } } // 这个middleware处理在其它middleware中出现的异常 // 并将异常消息回传给客户端:{ code: '错误代码', msg: '错误信息' } const errorHandler = (ctx, next) => { return next().catch(err => { if (err.code == null) { logger.error(err.stack) } ctx.body = { code: err.code || -1, data: null, msg: err.message.trim() } ctx.status = 200 // 保证返回状态是 200, 这样前端不会抛出异常 return Promise.resolve() }) } module.exports = { responseHandler, errorHandler }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/7184c384eb999d012ea89bab6728701c.html