NodeJS在中间件领域有着较为广泛的应用,他能做一些中间层事件,把服务端一部分的代码抽出来,减少处理冗余事情付出的代价,同时让服务真正做业务处理而不用关心页面的事情
常见的应用场景有:
-
跨域:解决跨域问题,前端不需要任何相关配置(当然这部分代码可以放在服务端)
-
数据mock:前后端同步开发(这没什么好说的)
-
接口整合或拆分:中间层的功能是作为后端接口与客户端界面之间的桥梁。前端首先向中间层发起请求,中间层根据业务逻辑向指定后端服务发起请求,后端返回数据后, 中间层根据前端所需的数据格式、种类进行排序筛选等整合封装,最后返回给前端。
-
代理:在开发环境下,可以利用Node.js代理,解决最常见的跨域问题;在线上环境下,我们可以利用代理,转发请求到多个服务端,起到类似于Nginx的负载均衡作用,且Node.js中可以很方便的根据业务架构编写转发逻辑代码,相对Ngnix更加灵活。
-
缓存:缓存其实是更靠近前端的需求,用户的动作触发数据的更新,Node.js中间层可以直接处理一部分缓存需求,在Redis中获取热点数据或临时数据。而不需要再去请求后端和数据库。 - 限流:Node.js中间层里,可以针对接口或者路由做响应的限流逻辑开发。
-
限流:Node.js中间层里,可以针对接口或者路由做响应的限流逻辑开发。
-
日志:相比其他服务端语言,Node.js中间层的日志记录,能更方便快捷的定位问题系统问题是后台服务还是前端问题。
-
监控:利用Node.js擅长高并发的请求处理的特性,可以用来做服务流量监控入口监控。
-
鉴权:微服务架构加可以将身份认证,权限校验的功能统一提前到中间层,不需要在每个服务里都对请求鉴权,降低代码冗余。此外也可以在Node.js中实现针对不同用户转发到不同后端服务的逻辑,这也是灰度发布的一种实现方式。
-
路由:前端更需要掌握页面路由的权限和逻辑,在中间层中对接口数据、路由整合转发,可以实现前端路由和请求接口路由的一致性,而后端服务接口路由可以独立设计,不需要迁就某个客户端所需的路由。
我写了一个demo加深理解,首先解释一下代码目录
代码地址:github地址
看app.js
日志中间件
// 日志中间件
app.use(async (ctx, next) => {
const start = new Date();
let ms;
try {
await next();
ms = new Date() - start;
logger.accessLog(ctx, ms);
} catch (error) {
ms = new Date() - start;
logger.errorLog(ctx, error, ms);
}
});
每次请求都会往文件输出一条日志方便查找错误,使用的是npm包---log4js,具体封装见/src/logs/logs.js
跨域中间件
// middlewares
app.use(KoaCors())
body解析中间件
//body解析中间件
app.use(bodyParser())
路由中间件
import KoaRouter from '@koa/router'
//路由中间间,routers 是导出的函数
app.use(router.routes()).use(router.allowedMethods());
routers(router)
代码所有的目录入口都是index
import requireIndex from 'es6-requireindex'
import middleswares from '../middlewares';
const controller = require('../controllers');
const modulesFiles = requireIndex(__dirname).modules;
export default (router) => {
Object.keys(modulesFiles).forEach((item) => {
modulesFiles[item](router, middleswares, controller)
})
}
目的是为了配合koa的洋葱模型,
举个例子:
router.get('/department/list', middleswares.formatBody, controller.department.list);
在处理完路由之后先通过controller处理,然后执行formatBody,再把结果返回,所以写这个回调函数的时候要特别注意
/**
* 响应数据格式化输出中间件
*
*/
module.exports = async (ctx, next) => {
try {
await next();//执行路由 这个next函数就是控制器函数,意思先执行处理逻辑,把结果根据自己的格式返回
if (ctx.body) {
ctx.body = {
code: ctx.response.status,
success: true,
message: 'success',
data: ctx.body
}
} else {
ctx.body = {
code: ctx.response.status,
success: true,
message: 'success',
}
}
} catch (err) {
console.log(err);
ctx.response.status = err.response.status;
ctx.body = {
success: false,
message: err.response.data.text,
};
}
}
不需要经过中间件处理的就写成这样就可以
router.get('/user', controller.login.userInfo);