快速的利用 Express 框架實現一個 Rustfull 接口的后端 Server
附:基于 Nodejs 的服務器后端的框架用的比較多的是 Koa : github/kaojs/koa 但本文這里介紹的基于 Express 框架。
附錄: 前端開發,和一般開發 (非官方說法 ) 有三個階段:
基于代碼 Code 的 log 打印階段調試開發 基于 IDE,如(Vscode) 的代碼調試,斷點階段。 基于測試用例 (TDD) 驅動開發的測試驅動開發階段,針對前端的或 nodejs 的項目,推薦使用Facebook 開源的 Jest 包做單元測試和測試驅動。
REST 介紹
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包生成一份貢獻者契約,這會讓你的項目更受貢獻者的歡迎。
創建一個 pcxProject 文件夾
mkdir pcxProject
轉入你剛剛創建的目錄
cd pcxProject
創建package.json文件
npm init
你將會看到類似于下面這樣的結果:
輸入yes,按下enter鍵來完成package.json文件的創建。
創建server.js文件
touch server.js
在這個server中,我們將會寫下創建我們server的代碼。創建一個api文件夾mkdir api
在這個api文件夾中,創建三個獨立的models、routes、以及controllers文件夾
mkdir api/controllers api/models api/routes
在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文件將會被修改,添加兩個新的依賴:
打開 package.json 文件,并且把這個任務添加到文件中 。
"start" : "nodemon server.js"
打開 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
) ;
在你的終端中運行 npm run start,啟動你的server,你將會看到如下 Log.
todo list RESTful API server started on: 3000
安裝數據庫
mongoDB
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
) ;
MySqL
這里使用 promise-mysql 可以避免寫回調函數。
yarn add promise-mysql
在程序服務啟動之前,在 config 文件加下面添加一個數據庫初始化的操作,touch promisePool.js 數據庫連接池。
const mysql
= require ( "promise-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 ( ) ;
}
async function getAllAddr ( tableName
, coinType
) { let connection
= await getConnection ( ) ; try { let sqlStr
= `select * from ${ tableName} where cointype=' ${ coinType} ';` console
. log ( sqlStr
) const result
= await connection
. query ( sqlStr
) return result
; } finally { connection
. release ( ) ; }
}
Redis
const redis
= require ( "async-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
) ; 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' ) ; app
. 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 定時器任務
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 ) 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
++ 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 ) { await 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 ) let nodeStatus
= new Server. RespParam. NodeStatusReq ( ) nodeStatus
. msgid
= Server
. RespParam
. GetMsgId ( ) nodeStatus
. coinType
= Server
. ServerData
. PCX const 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 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。