node+koa中转层开发实践总结
node中转层的意义:
1.能解决前后端代码部署在不同服务器下时的跨域问题。(实现)
2.合并请求,业务逻辑处理。(实现)
3.单页应用的首屏服务端渲染。(暂未实现)
环境准备:
node: ^8.11.2
koa: ^2.6.1
koa-router: ^7.4.0
koa-bodyparser: ^4.2.1
在项目目录下新建server目录,新建app.js
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const apiControler = require('./apiControler'); let app = new Koa();
global.hostname = "172.16.16.113";
global.port = 8070; app.use(async (ctx, next) => {
await next();
console.log(`Process ${ctx.request.method} ${ctx.request.url}...`);
}); //bodyParser必须在router之前注册到app上
app.use(bodyParser()); //使用路由处理中间件
app.use(apiControler()); app.listen(8899);
在server目录下新建业务接口目录,本例目录名为apiControlers,拿登录模块为例,新建一个login.js,里面包含登录模块所需要的所有接口。(获取验证码、登录、获取菜单权限)
login.js
const http = require('http');
const hostname = global.hostname;
const port = global.port;
let tokenStr = ""; /*获取图形验证码*/
let getAuthCoedFn = async (ctx, next) => {
let data = await asyncGetAuthCode();
ctx.set("Content-Type", "application/json");
ctx.body = JSON.parse(data);
await next();
};
function asyncGetAuthCode() {
return new Promise((resolve, reject)=> {
let authCodedData = "";
let req = http.request({
path: '/api/backstage/authCode',
port: port,
method: 'GET',
hostname: hostname
},(res)=> {
res.on('data', (chunk)=> {
authCodedData += chunk
});
res.on('end', ()=> {
authCodedData = JSON.stringify(authCodedData)
resolve(authCodedData)
})
});
req.on("error", (e)=> {
console.log("api:/backstage/authCode error")
reject(e.message)
});
req.end();
})
} /*登录*/
let loginFn = async (ctx, next) => {
let param = ctx.request.body;
let authcodekey = ctx.request.header.authcodekey;
let postData = {
userName: param.userName,
authCode: param.authCode,
password: param.password
};
let loginData = await asyncPostLogin(authcodekey, JSON.stringify(postData));
ctx.set("Content-Type", "application/json");
ctx.set("Connection", "keep-alive");
ctx.body = JSON.parse(loginData);
next()
};
function asyncPostLogin(authcodekey, postData) {
return new Promise((resolve, reject)=> {
let loginData = "";
let req = http.request({
path: '/api/backstage/login',
port: port,
method: 'POST',
hostname: hostname,
headers: {
'Content-Type': 'application/json',
'authCodeKey': authcodekey
}
},(res)=> {
res.on('data', (chunk)=> {
loginData += chunk
}).on('end', ()=> {
loginData = JSON.stringify(loginData);
tokenStr = res.headers['set-cookie'];
resolve(loginData)
})
});
req.on('error', (e)=> {
console.log("api:/backstage/login error");
reject(e.message)
});
req.write(postData);
req.end();
})
} /*获取菜单及权限列表*/
let getPowerListFn = async (ctx, next) => {
let menuList = await asyncGetPowerList();
ctx.body = JSON.parse(menuList);
next()
};
function asyncGetPowerList() {
return new Promise((resolve, reject)=> {
let listData = "";
let req = http.request({
path: '/api/backstage/getPowerList',
method: 'get',
port: port,
hostname: hostname,
headers: {
'Cookie': tokenStr.toString()
}
},(res)=> {
res.on('data', (chunk)=> {
listData += chunk;
}).on('end', ()=> {
listData = JSON.stringify(listData);
resolve(listData)
})
});
req.on("error", (e)=> {
console.log("api: /backstage/getPowerList error");
reject(e.message)
});
req.end()
})
} module.exports = {
'GET/api/backstage/authCode': getAuthCoedFn,
'POST/api/backstage/login': loginFn,
'GET/api/backstage/getPowerList': getPowerListFn
}
以接口功能声明一个函数,在此函数中通过node的http模块发送请求。需要注意的是http.request请求获取响应头cookie的方式是tokenStr = res.headers['set-cookie']。
每一个业务功能js最后暴露出内部所有以接口请求方式+接口地址为key,以对应功能函数为value的对象。
在server目录下新建一个apiControler.js中间件(有返回值的函数)。此中间件的功能一是读取apiControlers目录下的所有业务js,并引入;二是设置接口请求方式与执行函数的映射关系。
最后暴露出一个函数返回所有请求接口路径的集合。
apiControler.js
const fs = require("fs"); function readApiFiles(router, dir = '/apiControlers') {
fs.readdirSync(__dirname + dir).filter((f)=> {
return f.endsWith('.js')
}).forEach(f => {
console.log(`process controller: ${f}...`);
let mapping = require(__dirname + dir + '/' + f);
addMapping(router, mapping)
});
} function addMapping(router, mapping) {
for(let url in mapping) {
if(url.startsWith('GET')) {
let path = url.substring(3);
router.get(path, mapping[url]);
}else if(url.startsWith('POST')) {
let path = url.substring(4);
router.post(path, mapping[url]);
}else{
router.get(url, mapping[url]);
console.log(`无效的URL: ${url}`);
}
}
} module.exports = function (dir) {
let controllers_dir = dir || '/apiControlers';
let router = require('koa-router')();
readApiFiles(router, controllers_dir);
return router.routes();
};
最后回到app.js,引入apiControler.js中间件并注册到app上。需要注意的是bodyParser中间件必须在router之前注册到app上。
后续
此例目前只能用作接口转发、合并请求和解决跨域问题,终极目标是能解决SPA(单页应用的)首屏服务端渲染问题。
持续折腾中...
node+koa中转层开发实践总结的更多相关文章
- Docker + node(koa) + nginx + mysql 开发环境搭建
什么是Docker Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源. Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然 ...
- Docker + node(koa) + nginx + mysql 线上环境部署
在上一篇 Docker + node(koa) + nginx + mysql 开发环境搭建,我们进行了本地开发环境搭建 现在我们就来开始线上环境部署 如果本地环境搭建没有什么问题,那么线上部署的配置 ...
- 基于 Angularjs&Node.js 云编辑器架构设计及开发实践
基于 Angularjs&Node.js 云编辑器架构设计及开发实践 一.产品背景 二.总体架构 1. 前端架构 a.前端层次 b.核心基础模块设计 c.业务模块设计 2. Node.js端设 ...
- Angular开发实践(七): 跨平台操作DOM及渲染器Renderer2
在<Angular开发实践(六):服务端渲染>这篇文章的最后,我们也提到了在服务端渲染中需要牢记的几件事件,其中就包括不要使用window. document. navigator等浏览器 ...
- MVC5 网站开发实践 概述
目录 MVC5 网站开发实践 概述 MVC5 网站开发实践 1.建立项目 MVC5 网站开发实践 2.后台管理 MVC5 网站开发实践 2.1.管理员登陆 MVC5 网站开发实践 2.2.管理 ...
- ASP.NET MVC5 网站开发实践(一) - 框架(续) 模型、数据存储、业务逻辑
上次搭建好了项目框架,但还是觉得不太对劲,后来才想起来没有对开发目标进行定位,这个小demo虽然不用做需求分析,但是要实现什么效果还得明确.后来想了一下就做个最简单的网站,目标定为小公司进行展示用的网 ...
- ASP.NET MVC5 网站开发实践(一) - 项目框架
前几天算是开题了,关于怎么做自己想了很多,但毕竟没做过项目既不知道这些想法有无必要,也不知道能不能实现,不过邓爷爷说过"摸着石头过河"吧.这段时间看了一些博主的文章收获很大,特别是 ...
- ASP.NET MVC5 网站开发实践 - 概述
前段时间一直在用MVC4写个网站开发的demo,由于刚开始学所有的代码都写在一个项目中,越写越混乱,到后来有些代码自己都理不清了.1月26日晚上在群里跟@怒放 他们讨论这个问题,结论是即使只是一个小d ...
- vue2.0 开发实践总结之入门篇
vue2.0 据说也出了很久了,博主终于操了一次实刀. 整体项目采用 vue + vue-router + vuex (传说中的vue 全家桶 ),构建工具使用尤大大推出的vue-cli 后续文 ...
随机推荐
- 【MongoDB-query查询条件】
在上一篇中简要使用了C# 对MongoDB进行数据操作,这里补充一些MongoDB query查询条件文档: Query.All("name", "a",&qu ...
- IIS日志自动清理
IIS在运行的过程中日志会不停地增长,若iis的网站被频繁的调用或不当的调用,则会产生很多日志.我在系统运维的时候曾出现过20G的系统盘,由于合作商开发的程序有问题,每几百微秒调用一次web服务,短期 ...
- C# 四舍五入 保留两位小数(转载)
一.C#默认四舍五入 1 Math.Round(45.367,2) //Returns 45.372 Math.Round(45.365,2) //Returns 45.36二.C#中的Round() ...
- springboot自定义banner生成器
http://patorjk.com/software/taag/#p=display&f=Graffiti&t=Type%20Something%20
- Asp.Net里关于Session过期跳转页面的一些小技巧
这里算是自己的个人随笔吧,仅供参考使用,后续有更好的方法再做补充 之前在Aspx页面里面,在Session过期的时候我经常会使用 Server.Transfer("b.aspx") ...
- HDU2859(KB12-Q DP)
Phalanx Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- 原生css 中变量的使用
前两天看到阮大神的一篇在css中使用变量的文章,整理了一下. 这个重要的 CSS 新功能,所有主要浏览器已经都支持了.本文全面介绍如何使用它,你会发现原生 CSS 从此变得异常强大. 一.变量的声明 ...
- [基础知识]在PeopleSoft中SMTP设置不生效如何查找问题
在PeopleSoft中如果配置了工作流邮件或者标准页面的通知,都是可以发送出邮件的,这些邮件都是由SMTP服务器发送.SMTP需要在APP服务器和PRCS服务器中配置. 如果无法从PeopleSof ...
- 学习笔记(3)——实验室集群WMS服务配置
1.启动mgt结点的tomcat服务: [root@mgt zmq]# /home/geohpc/softwares/apache-tomcat-/bin/startup.sh 关闭为 [root@m ...
- Origin绘制双Y轴图的方法
1.已有数据绘图如下,其中网络流量的单位是M Bytes/s,与另外两组数据的单位(时间)不同,现在要为其添加右侧Y轴. 2.首先选中该图像,找到工具条,点击第三个按钮“Add Right-Y Lay ...