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中转层开发实践总结的更多相关文章

  1. Docker + node(koa) + nginx + mysql 开发环境搭建

    什么是Docker Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源. Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然 ...

  2. Docker + node(koa) + nginx + mysql 线上环境部署

    在上一篇 Docker + node(koa) + nginx + mysql 开发环境搭建,我们进行了本地开发环境搭建 现在我们就来开始线上环境部署 如果本地环境搭建没有什么问题,那么线上部署的配置 ...

  3. 基于 Angularjs&Node.js 云编辑器架构设计及开发实践

    基于 Angularjs&Node.js 云编辑器架构设计及开发实践 一.产品背景 二.总体架构 1. 前端架构 a.前端层次 b.核心基础模块设计 c.业务模块设计 2. Node.js端设 ...

  4. Angular开发实践(七): 跨平台操作DOM及渲染器Renderer2

    在<Angular开发实践(六):服务端渲染>这篇文章的最后,我们也提到了在服务端渲染中需要牢记的几件事件,其中就包括不要使用window. document. navigator等浏览器 ...

  5. MVC5 网站开发实践 概述

    目录 MVC5 网站开发实践  概述 MVC5 网站开发实践  1.建立项目 MVC5 网站开发实践  2.后台管理 MVC5 网站开发实践  2.1.管理员登陆 MVC5 网站开发实践 2.2.管理 ...

  6. ASP.NET MVC5 网站开发实践(一) - 框架(续) 模型、数据存储、业务逻辑

    上次搭建好了项目框架,但还是觉得不太对劲,后来才想起来没有对开发目标进行定位,这个小demo虽然不用做需求分析,但是要实现什么效果还得明确.后来想了一下就做个最简单的网站,目标定为小公司进行展示用的网 ...

  7. ASP.NET MVC5 网站开发实践(一) - 项目框架

    前几天算是开题了,关于怎么做自己想了很多,但毕竟没做过项目既不知道这些想法有无必要,也不知道能不能实现,不过邓爷爷说过"摸着石头过河"吧.这段时间看了一些博主的文章收获很大,特别是 ...

  8. ASP.NET MVC5 网站开发实践 - 概述

    前段时间一直在用MVC4写个网站开发的demo,由于刚开始学所有的代码都写在一个项目中,越写越混乱,到后来有些代码自己都理不清了.1月26日晚上在群里跟@怒放 他们讨论这个问题,结论是即使只是一个小d ...

  9. vue2.0 开发实践总结之入门篇

    vue2.0 据说也出了很久了,博主终于操了一次实刀. 整体项目采用  vue +  vue-router +  vuex (传说中的vue 全家桶 ),构建工具使用尤大大推出的vue-cli 后续文 ...

随机推荐

  1. nodejs的__dirname,__filename,process.cwd()区别

    假定我们有这样一个mynode的node项目在User/leinov/porject/文件夹下,cli是一个可执行文件 |-- mynode |-- bin |-- cli.js |-- src |- ...

  2. 深入出不来nodejs源码-timer模块(C++篇)

    终于可以填上坑了. 简单回顾一下之前JS篇内容,每一次setTimeout的调用,会在一个对象中添加一个键值对,键为延迟时间,值为一个链表,将所有该时间对应的事件串起来,图如下: 而每一个延迟键值对的 ...

  3. 如何查看是否安装.NET Framework、.NET Framework的版本号以及CLR版本号

    查看是否安装.NET Framework→%SystemRoot%\System32→如果有mscoree.dll文件,表明.NET Framework已安装 查看安装了哪些版本的.NET Framw ...

  4. tomcat 防火墙如何设置

    tomcat 防火墙能够有效的防护我们电脑,那么我们要怎么样去设置呢?下面由学习啦小编给你做出详细的tomcat 防火墙设置方法介绍!希望对你有帮助! tomcat 防火墙设置方法一: 1.为tomc ...

  5. 学习Memcached:1基本配置与安装

    今天把刚刚学习的到memcached写在博客里,以免以后自己遗忘. 1.首先下载Memcached数据库服务文件,这是我下载好的这个memcached文件. 2.接下来就启动这个exe.通常我需要讲这 ...

  6. SpringBoot结合swagger2快速生成简单的接口文档

    1. pom.xml中加入依赖 <dependency> <groupId>com.spring4all</groupId> <artifactId>s ...

  7. Selenium使用总结(Java版本)

    硒在最近的发展中被广泛应用,因为它以前没有被使用过,并且已经走了太多的坑.这是一张唱片. 1.环境配置 配置要点: 1.Web驱动程序应该与浏览器版本相对应,chrome使用chrome驱动程序和ch ...

  8. 设计模式-原型(prototype)

     一.概念 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 二.模式动机 当已有一个对像,暂且称之为原型对象,需要一个新的对像,该对像和已有的原型对像具有相同的类型,且里面的属性大部分 ...

  9. Java - Map总结

    Java提高篇(三三)-----Map总结 一.Map概述 首先先看Map的结构示意图 Map:“键值”对映射的抽象接口.该映射不包括重复的键,一个键对应一个值. SortedMap:有序的键值对接口 ...

  10. DOM相关

    归纳一下, 不管是DOM Core还是HTML-DOM,我们在使用JavaScript的时候要注意浏览器之间的兼容性,因为不同的浏览器对这两类方法和属性的支持可能不一样,一般推荐使用DOM Core方 ...