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 后续文 ...
随机推荐
- MVC应用程序使用jQuery接收Url的参数
在这个练习<MVC应用jQuery动态产生数据>http://www.cnblogs.com/insus/p/3410138.html 中,学会了使用jQuery创建url链接,并设置了参 ...
- 15天学习MVC后的小结(分享经历与想法)
学习MVC已经有半个月,看了看日历,刚好半个月.分享了好几篇练习的博文:一,<创建第一个MVC应用程序> http://www.cnblogs.com/insus/p/3358560.ht ...
- Spring基础(9) : 自动扫描
一 配置xml方式:扫描com包下的bean <?xml version="1.0" encoding="UTF-8" ?> <beans ...
- 定时器--Quartz.Net
这次由于项目的需求:什么定时发送邮件通知,定时筛选取消客户下单未支付的订单 重新捡起定时器,在网上翻来找去找到----Quartz.Net老字号了并不表示它就真的老了哦 github:https:// ...
- 微软宣布.NET开发环境将开源 支持三大操作系统(windows,Mac OS X和Linux)(转)
微软周三(11月12日)公布了.NET开发框架开源计划.公司拟将这长期以来只能运行于Windows系统下的开发环境,通过GitHub开源,以实现跨平台支持Mac OS X和Linux.根据微软公布的计 ...
- java多线程框架
JDK5中的一个亮点就是将Doug Lea的并发库引入到Java标准库中.Doug Lea确实是一个牛人,能教书,能出书,能编码,不过这在国外还是比较普遍的,而国内的教授们就相差太远了. 一般的服务器 ...
- JDBC流程
最近要学习Mybatis的源码,因此对JDBC的使用进行总结. 一.JDBC概述 JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java ...
- SVN查看所有日志提交记录
1. svn默认显示最近一周的文件提交和修改记录,怎么查看更长时间的日志记录呢? 2. TortoiseSVN 3. 点击show all 或者NEXT 100,就可显示更长时间的文件提交记录.
- unity中生成一个GUI格子(始终居中)
1.Script程序 using UnityEngine; using System.Collections; public class GUITest : MonoBehaviour { [Seri ...
- js-ES6学习笔记-Promise对象
1.Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大. 2.所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作) ...