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 后续文 ...
随机推荐
- 用python写web一定要去破解的异步请求问题.经历web.py和tornado,完破!
1.问题 上个学期,给学校写了一个数据服务,主要从oracle里面读取一些数据供查询使用,非常快速的用web.py搭建了起来.调试顺利,测试正常,上线!接下来就是挨骂了,我铁定知道会卡,但是没想到会那 ...
- jQuery自定义数组操作类(类似于List集合的增删改查)
js外部文件,前提需要引入jquery类库. 封装类代码如下: (function ($) { $.List = function () { var _list = new A ...
- Java并发编程-闭锁
闭锁是一种同步器 ( Synchronizer ),它可以延迟线程的进度直到线程到达终止状态,CountDownLatch是一个灵活的闭锁实现:1)允许一个或多个线程等待一个事件集的发生,闭锁的状态包 ...
- 啰里吧嗦CountDownLatch
java.util.concurrent Class CountDownLatch 目录 CountDownLatch 是什么 CountDownLatch是一个同步工具类,它允许一个或多个线程一直等 ...
- linux系统编程:自己动手写一个ls命令
ls用于列举目录内容,要实现这个功能,毫无疑问,需要读取目录,涉及到两个api: opendir:DIR *opendir(const char *name), 传文件名,返回一个指针,指向目录序列 ...
- 关于CSS和JS中用到的各种Height和Width的问题
自己记不住,列一下关于CSS和JS中用到的各类有关Height和Width属性的介绍对比. 所属类别 属性名 意义 其他 浏览器模型 Screen.height 浏览器窗口所在的屏幕的高度(单位像素) ...
- BZOJ3108 [cqoi2013]图的逆变换
Description 定义一个图的变换:对于一个有向图\(G=(V, E)\),建立一个新的有向图: \(V'=\{v_e|e \in E\}\),\(E'=\{(v_b, v_e)|b=(u,v) ...
- 【读书笔记】iOS-网络-Cookie
Cookie是HTTP协议在首个版本之后加入的一个重要组件.它向服务器提供了追踪会话状态的能力,同时又无须维持客户端与服务器之间的连接.在浏览器客户端,Cookie值是由服务器通过请求提供的,,然后被 ...
- phpstudy集成下Apache配置部署https安全证书
一..先申请到安全证书.(腾讯云或者阿里云申请免费1年的安全证书),怎么申请这里也说下(以腾讯云为例): 1.登录腾讯云QQ或微信登录都行,第一次登录要通过实名认证,点击[产品]---[ss证书l] ...
- JavaSE——线程同步
为什么需要线程同步? 同步就是协同步调,按预定的先后次序进行运行.如:你说完,我再说而并非一起动作.“同”字应是指协同.协助.互相配合. 如进程.线程同步,可理解为进程或线程A和B一块配合,A执行到一 ...