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 后续文 ...
随机推荐
- 我进行jvm内存调优的一些记录
jvm内存调优的一些记录 java内存调优的方法和过程 可以使用 jmap -heap pid号 查看,例如pid是9300,执行的结果可能是这样的. root@ubuntu:~# jmap -hea ...
- 微信小程序入门篇
微信小程序入门篇: 准备工作 IDE搭建 就不多说了,没有内测码去下载个破解版吧,我用了一下,学习完全够了!IDE破解版+安装教程 图片发自简书App 知识准备 JavaScrip还是要看看的,推荐教 ...
- 转载 HashSet用法
NET 3.5在System.Collections.Generic命名空间中包含一个新的集合类:HashSet<T>.这个集合类包含不重复项的无序列表.这种集合称为“集(set)”.集是 ...
- 如何监听对 HIVE 元数据的操作
目录 简介 HIVE 基本操作 获取 HIVE 源码 编译 HIVE 源码 启动 HIVE 停止 HIVE 监听对 HIVE 元数据的操作 参考文档 简介 公司有个元数据管理平台,会定期同步 HIVE ...
- 华中农业大学第五届程序设计大赛网络同步赛-G
G. Sequence Number In Linear algebra, we have learned the definition of inversion number: Assuming A ...
- MySql处理函数
Lower(name) 转换为小写 Upper(name)转换为大写 Substr(name,length,index):从index开始截取length个字符串 Length(name) 获取长度 ...
- PHP实现大转盘抽奖算法
流程: 1.拼装奖项数组,2.计算概率,3.返回中奖情况 代码如下:中奖概率 ' v ' 可以在后台设置,传到此方法中,注意传整数 function get_gift(){ //拼装奖项数组 // 奖 ...
- BZOJ5289: [Hnoi2018]排列
传送门 第一步转化,令 \(q[p[i]]=i\),那么题目变成: 有一些 \(q[a[i]]<q[i]\) 的限制,\(q\) 必须为排列,求 \(max(\sum_{i=1}^{n}w[i] ...
- ss 重新设置 端口的方法 记录
1. 选择 ssh 进行远程登入: ssh root@服务器ip -p 端口, 事例如:ssh root@176.122.134.96 -p 28202 2. ls 展示 当前目录下的文件,看到有 s ...
- Jaguar_websocket结合Flutter搭建简单聊天室
1.定义消息 在开始建立webSocket之前,我们需要定义消息,如:发送人,发送时间,发送人id等.. import 'dart:convert'; class ChatMessageData { ...