快速的利用 Express 框架实现一个 Rustfull 接口的后端 Server

附:基于 Nodejs 的服务器后端的框架用的比较多的是 Koa : github/kaojs/koa 但本文这里介绍的基于 Express 框架。

附录: 前端开发,和一般开发 (非官方说法) 有三个阶段:

  • 基于代码 Code 的 log 打印阶段调试开发
  • 基于 IDE,如(Vscode) 的代码调试,断点阶段。
  • 基于测试用例 (TDD) 驱动开发的测试驱动开发阶段,针对前端的或 nodejs 的项目,推荐使用Facebook 开源的 Jest 包做单元测试和测试驱动。

REST 介绍

  • REST是Representational State Transfer的缩写。它是Web标准架构和HTTP协议。REST架构风格描述了Roy Fielding在博士论文中最初表达的六个限制,并将RESTful风格的基础定义为:

    • 统一的界面
    • 无状态
    • 可缓存
    • 客户端服务器
    • 分层系统
    • 按需编码

RESTful 的应用程序使用HTTP执行四种操作CRUD(C:create,R:read,U:update,D:delete)。Create和Update用来提交数据,get来读取数据,delete用来移除数据。RESTful由基本URL,URL,媒体类型等组成。

使用的工具

  • Node.js
  • MySqL
  • Redis
  • Ticker (定时器)
  • Postman (测试接口)

构建工程

注:如果你的需求是基于 express 建立一个 Web 项目,建议可以使用官方的推荐框架生成器,通过应用生成器工具 express-generator 可以快速创建一个应用的骨架。具体可以参见:http://www.expressjs.com.cn/starter/generator.html

  • 进入你的工程目录,打开你的终端并且进行以下步骤:

首先做下面的准备工作。

  • npx license mit 通过license包下载对应的协议npx gitignore node使用gitignore包自动的从Github仓库中下载相关文件
  • npx covgen使用covgen包生成一份贡献者契约,这会让你的项目更受贡献者的欢迎。
  1. 创建一个 pcxProject 文件夹
mkdir pcxProject
  1. 转入你刚刚创建的目录
cd pcxProject
  1. 创建package.json文件
npm init
  • Package.json文件告诉了npm必要的信息,让npm可以识别项目,同时处理项目的依赖。

  • npm初始化将会提示你输入一些必要信息,比如说app名称、描述、版本、作者、关键字以及你是否相应看见你喜欢的。

你将会看到类似于下面这样的结果:

  • 输入yes,按下enter键来完成package.json文件的创建。
  1. 创建server.js文件
touch server.js
  • 在这个server中,我们将会写下创建我们server的代码。创建一个api文件夹mkdir api
  1. 在这个api文件夹中,创建三个独立的models、routes、以及controllers文件夹
mkdir api/controllers api/models api/routes
  1. 在api/controller文件夹中创建 Controller.js文件,在routes文件中创建 Routes.js 文件,在models文件夹中创建 Model.js 文件
  • 我们的文件目录应该像下面这样:

  • 其中,config 文件下面主要是程序启动之前的初始化操作。如定义的初始化数据在 Express Server 启动之前的初始化操作,或者一些数据库连接的初始化。

Express Server 安装及开发

  • 现在来安装express和nodmon,express用来创建server,nodmon帮助我们追踪我们应用程序的改变,它会监视文件的改变并且自动重启server。
npm install --save-dev nodemon
npm install express --save
  • 一旦成功安装之后,你的package.json文件将会被修改,添加两个新的依赖:
  1. 打开 package.json 文件,并且把这个任务添加到文件中 。

"start" : "nodemon server.js"

  1. 打开 server.js 文件,启动 Express 可以采用如下的代码:
var express = require('express'),app = express(),port = process.env.PORT || 3000;app.listen(port);console.log('todo list RESTful API server started on: ' + port);
  1. 在你的终端中运行 npm run start,启动你的server,你将会看到如下 Log.

todo list RESTful API server started on: 3000

安装数据库

mongoDB

  • 首先,我们要安装mongoose

yarn add mongoose

  • Mongoose 是我们用来与 MongoDB 数据库交互的工具。安装之后,打开 Model.js 文件,输入以下代码并且保存:
'use strict';
var mongoose = require('mongoose');
var Schema = mongoose.Schema;var TaskSchema = new Schema({name: {type: String,Required: 'Kindly enter the name of the task'},Created_date: {type: Date,default: Date.now},status: {type: [{type: String,enum: ['pending', 'ongoing', 'completed']}],default: ['pending']}
});module.exports = mongoose.model('Tasks', TaskSchema);
  • 通过上面的代码,我们在我们的文件导入了mongoose,我们创建了一个模型,说明我们的数据集应该呈现的样子。

  • 正如你所看到的,它指出了数据集(表)应该包含一个名称,它的类型应该是string,还包括了创建日期。它也包含了任务状态,每个任务被创建的时候都有默认值pending。

MySqL

  • 这里使用 promise-mysql 可以避免写回调函数。
yarn add promise-mysql
  • 在程序服务启动之前,在 config 文件加下面添加一个数据库初始化的操作,touch promisePool.js 数据库连接池。
const mysql= require("promise-mysql");// For Mysql
const pool = mysql.createPool({host: config.DBConfig.DBAddr,port: config.DBConfig.Port,user: config.DBConfig.UserName,password: config.DBConfig.Password,database: config.DBConfig.DbName,connectionLimit: 10,
});async function getPool() {return pool;
}async function getConnection() {return (await pool).getConnection();
}
  • 在使用的时候,直接可从连接池获取一个连接。

  • 在 config 文件夹的另外一个文件 initServer.js 如其名字一样,提供了初始化的变量的一些工作和导出函数。

  • 之外,在 model.js 中封装了要使用的函数,比如:导出 getAllAddr( )

// For Database
async function getAllAddr(tableName,coinType){let connection = await getConnection();try {let sqlStr = `select * from ${tableName} where cointype='${coinType}';`// var sqlStr = sqlString.format('SELECT * FROM walletaddress WHERE cointype= ?',[coinType])console.log(sqlStr)const result = await connection.query(sqlStr)// console.log(result)return result;} finally {connection.release();}
}
  • 其他,MySql 获取数据的的接口,如上即可。

Redis

  • Redis 这里采用 async-redis 异步的连接 redis 的库。

  • 在 config/promisePool.js 中加入 Redis 的初始化函数。

const redis = require("async-redis");// For redis
const ccIp = config.CCConfig.RedisAddr.split(':',2)[0]
const ccPort = config.CCConfig.RedisAddr.split(':',2)[1]
const client = redis.createClient(ccPort,ccIp,{auth_pass: config.CCConfig.Password})async function ccInit() {await client.on("error", function (err) {console.log("Error " + err);});
}var redisDao = function() {};redisDao.prototype.get = async function (key){let resp = await client.get(key);console.log('AfterGet:', resp)return resp
}redisDao.prototype.set = async function (key, value){await client.set(key, value)
}redisDao.prototype.del = async function (key){await client.del(key, function(err, o) {console.log("Del error: " + err);})
}redisDao.prototype.hgetall = async function(key) {let resp = await client.hgetall(key);// console.log('AfterGetAll:',resp)return resp
}redisDao.prototype.hmset = async function(Item, key, value) {await client.hmset(Item,key,value)
}redisDao.prototype.hdel = async function(Item, key) {console.log("DelHash Item:%s,key:%s", Item, key)await client.hdel(Item, key)
}

安装路由

  • 路由决定了应用程序如何响应特定终端的客户端的请求,一个特定的URL(或path)以及特定的HTTP请求(GET、POST等等)。

  • 每个路由都有不同的路由处理方法,当路由匹配时被执行。下面我们将会用不同的方法定义两个基本的路由(“tasks”以及“/tasks/taskId”)。

  • “/tasks”有POST和GET方法,“/tasks/taskId”有GET、PUT和DELETE方法。

  • 正如你所看到的,我们需要controller,每个路由方法都能调用它对应的处理程序。controller 下面可以写业务相关的 Restfull api 的响应处理函数。

use strict';
module.exports = function(app) {var todoList = require('../controllers/todoListController');// todoList Routesapp.route('/tasks').get(todoList.list_all_tasks).post(todoList.create_a_task);app.route('/tasks/:taskId').get(todoList.read_a_task).put(todoList.update_a_task).delete(todoList.delete_a_task);
};

安装控制器

  • 打开todoListController.js文件,然后进行下一步:

  • 在这个controller中,我们将会写出5个不同的方法:list_all_tasks, create_a_task,read_a_task,update_a_task, delete_a_task。我们将会到处每个方法。

  • 每个方法会用到不同的mongoose方法,例如find、findById、findOneAndUpdate、save 以及 remove。

'use strict';var mongoose = require('mongoose'),Task = mongoose.model('Tasks');exports.list_all_tasks = function(req, res) {Task.find({}, function(err, task) {if (err)res.send(err);res.json(task);});
};exports.create_a_task = function(req, res) {var new_task = new Task(req.body);new_task.save(function(err, task) {if (err)res.send(err);res.json(task);});
};exports.read_a_task = function(req, res) {Task.findById(req.params.taskId, function(err, task) {if (err)res.send(err);res.json(task);});
};exports.update_a_task = function(req, res) {Task.findOneAndUpdate(req.params.taskId, req.body, {new: true}, function(err, task) {if (err)res.send(err);res.json(task);});
};exports.delete_a_task = function(req, res) {Task.remove({_id: req.params.taskId}, function(err, task) {if (err)res.send(err);res.json({ message: 'Task successfully deleted' });});
};

schedule 定时器任务

  • 这里采用 while(true) + sleep 实现定时器,此处有两个定时器,两个 while(true) 但是考虑到 nodejs 的单线程异步的特性,当遇到需要同步的函数或阻塞的操作或 IO 操作的时候,如网络请求的操作时,nodejs 便会切换到其他地方去执行。除非是,await promise 操作才会等待。

  • 在scheduleTask 文件夹下面的 task.js 里面放入如下内容:

const schedule = require('./schedule')async function startQueryBlock() {try {schedule.ParseBlockTicker()schedule.HeartBeatTicker()} catch (error) {console.error(error);}
}module.exports = {startQueryBlock
}
  • 在具体的 Schedule 中完成特定的定时任务的操作。(如下)
const ParseBlockTicker = async () => {while(true) {await sleep(2000)  // Todo ****    // console.log('scheduleCronstyle:' + new Date());const block = await Server.ServerData.api.rpc.chain.getBlock();const lastHeight = parseInt(block.block.header.blockNumber.words[0]) if(lastHeight <= Server.ServerData.currentHeight) {continue} else {while(Server.ServerData.currentHeight < lastHeight) {Server.ServerData.currentHeight ++  // Todo if(Server.ServerData.currentHeight % 100 == 0) {console.log("[PCX] update node block info: node height is %d,mgr height is %d",lastHeight, Server.ServerData.currentHeight)}while(true) {err = await syncTo()if( err != null) {// if err happen, sleep 5 second retryawait sleep(5000);console.log("[PCX] fail to parse block and will try again. height : %d, err : %s", Server.ServerData.currentHeight, err);continue}break}await PromisePool.redisDao.set(Server.ServerData.PCX + 'HEIGHT', Server.ServerData.currentHeight)}}}
}const HeartBeatTicker = async () => {while(true) {await sleep(30000)// console.log('heartBeatTicker:' + new Date());let nodeStatus = new Server.RespParam.NodeStatusReq()nodeStatus.msgid = Server.RespParam.GetMsgId()nodeStatus.coinType = Server.ServerData.PCXconst block = await Server.ServerData.api.rpc.chain.getBlock();if(!block.block) {nodeStatus.message = Server.ServerData.PCX + Server.ServerData.NodeOfflineMessageconsole.log("[PCX] TRON node error, Report it!")let err = await reportSyncNodeStatus(nodeStatus)if(err != null) {console.log("reportNodeStatus failed")} else {console.log("reportNodeStatus successful.")}} else {nodeStatus.message = Server.ServerData.PCX + Server.ServerData.NodeAliveMessage}}
}

整合

  • 安装 bodyParser, 使用 bodyParser 在处理之前,先在中间层解析请求中的body,在req.body属性。它暴露了各种工厂来创建中间件。如果没有body来解析或者空对象({}),中间件将会使用解析的req.body属性来填充。

  • 关于主程序 Server.js 中可根据也业务调整如下:


var ServerInit = require("./config/initServer")
var express = require('express');
bodyParser = require('body-parser');
const parseBlock = require('./parseBlock/parseBlock')function startExpress() {app = express(),port = process.env.PORT ||  ServerInit.ServerData.HttpRestPort;app.use(bodyParser.urlencoded({ extended: true }));app.use(bodyParser.json());var routes = require('./api/routes/routes');routes(app);    app.use(function(req, res) {res.status(404).send({url: req.originalUrl + ' not found'})});app.listen(port);console.log("Server has starting")
}async function main() {await ServerInit.InitServer();startExpress();// 启动定时任务parseBlock.startScheduleTask();
}main();

通过Postman测试

用 Postman 测试,略。

快速的利用 Express 框架实现一个 Rustfull 接口的后端 Server相关推荐

  1. 如何利用laragon框架制作一个简单的应用?

    如何利用laragon框架制作一个简单的应用? 一.搭建环境 1. 安装Laragon 1.1 打开安装包用的语言 选择自己习惯用的语言 1.2 选择安装地址 1.3 选择Next,开始install ...

  2. [Web]如何利用Boostrap框架搭建一个还可以的静态网站(五_子页_脱发指南)

    文章目录 返回总结 整体效果 组件 人物介绍块 内容介绍块 代码 独属CSS HairLossGuide.css html HairLossGuide.html 返回总结 如何利用Boostrap框架 ...

  3. 通过python利用Django框架搭建一个属于自己的免费网站(已更新)

    大家好,我是天空之城,今天给大家带来,通过python利用Django框架搭建一个属于自己的免费网站. 以下是网上找到的免费教程,https://djangogirlstaipei.gitbooks. ...

  4. [Web]如何利用Boostrap框架搭建一个还可以的静态网站(六_子页)

    文章目录 返回总结 整体效果 组件 vedio题目 vedio博主介绍 vedio vedio用户留言 电子烟评测块 辩论块 代码 html ElectronicCigarettes.html 返回总 ...

  5. [Web]如何利用Boostrap框架搭建一个还可以的静态网站(四_子页_戒烟小说)

    文章目录 返回总结 整体效果 思路 组件 轮播图 排行榜 轮播广告语 广告 空行 小说块 代码 独属css SmokeNote_Style.css html SmokeNote.html 返回总结 如 ...

  6. [Web]如何利用Boostrap框架搭建一个还可以的静态网站(三_主页)

    文章目录 返回总结 整体效果 思路 各个组件代码 导航栏 LOGO 滚动时间 标题线 图词模块 页脚 代码 独属css文件 indexCss1.css html代码 index.html 返回总结 如 ...

  7. 遗传编程(Genetic Programming)学习笔记(三):利用DEAP框架创建一个GP表达式

    DEAP框架   Python 的 DEAP库是一个进化算法框架,可以帮助我们快速实现各种进化算法,例如遗传算法(GA).粒子群算法(PSO).遗传编程(GP).分布估计算法(EDA).多目标优化算法 ...

  8. 快速上手用Taro框架搭建一个微信小程序

    1.安装node版本管理工具https://www.runoob.com/nodejs/nodejs-install-setup.html 2.打开终端 3.打开终端后在命令行输入npm instal ...

  9. 利用wojilu框架仿一个网站的全过程(Step by Step利用wojilu框架开发网站系列二 附源码)...

    被仿的网站和仿照后的网站 被仿的网站-易读 仿照后的网站-我读 仿这个网站的目的:为了实践一下新学习的轻量级asp.net web开发框架[我记录] ,该网站比较简单,适合入门. 实现章节 章节部分, ...

最新文章

  1. Android TextVeiw 在java代码中添加空格的方法
  2. conda不是内部或外部命令
  3. oracle 用mybatis生成主键
  4. 性能测试-Jmeter
  5. VC++绘制铁路道岔
  6. java中自定义输入数字格式_Java 创建并使用自定义数字格式、35;###、####.#####和语言环境...
  7. 华科PAMI黑科技,方向任意目标检测新算法
  8. 我的docker随笔28:基于容器的升级方案实验
  9. 重置系统_【刹车系统】丰田锐志刹车系统重置记忆
  10. Python初学手记----在window系统中安装环境
  11. hibernate笔记--组合主键映射方法
  12. Java核心技术 卷1基础知识 原书第10版.pdf
  13. IE下判断IE版本的语句...[if lte IE 8]……[endif]
  14. 03 - Linux下安装软件的三种方式
  15. 虚拟机VMware16安装教程
  16. 各种梯度算法总结 + Total Variation
  17. 移动硬盘里面装linux系统,在移动硬盘上安装Ubuntu和其他linux系统-随身携带操作系统...
  18. 高质量代码的几大标准
  19. 浅析爱心代码的绘制思路——python实现
  20. 打开桌面计算机不显示文件夹,Win10系统怎么让此电脑中的文件夹不显示?

热门文章

  1. linux运维、架构之路-HAProxy反向代理
  2. Python之路 day1 基础1 变量 for while 用户输入
  3. 2016-5-31 问题及解决
  4. [codevs1262] 不要把球传我 数论+组合数学
  5. [转]Flex与.NET互操作(三):基于WebService的数据访问(下)
  6. AJAX ControlToolkit学习日志-Tabs(27)
  7. Hexo+GitHub 快速搭建个人博客(一)---- 基本部署
  8. Spring Boot 单例模式中依赖注入问题
  9. 一个注册为输入法的木马分析
  10. C++ 使用模板需要注意的事情