背景:
向小程序,提供http/https接口服務,存儲用戶數據、日誌數據。
技術選型
resitify
純粹的http服務,不涉及視圖
typescript 2.1+
類型校驗,規範代碼
mock.js
提供mock服務
mongodb
文檔結構,存儲數據
rollup
js管理
node
後端服務管理
pm2
後端node進程管理
環境搭建
安裝node
默認已安裝node,當前使用版本v10.16.3。
初始化工程
npm init -y
//得到如下package.json:
{
"name": "restify-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"
安裝依賴
//產線環境使用
npm install --save mongodb restify mockjs pm2
//開發環境使用
## 安裝 rollup.js 基礎模塊
npm i --save-dev rollup rollup-plugin-buble
## 安裝 rollup.js 編譯代碼混淆插件
npm i --save-dev rollup-plugin-uglify
## 安裝 rollup.js 編譯 Typescript 代碼的插件模塊
npm i --save-dev rollup-plugin-typescript typescript tslib
安裝後package.json
{
"name": "restify-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "rollup -c rollup.config.js",
"start":"node dist/app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"mockjs": "^1.1.0",
"mongodb": "^3.3.4",
"pm2": "^4.1.2",
"restify": "^8.4.0"
},
"devDependencies": {
"rollup": "^1.27.2",
"rollup-plugin-buble": "^0.19.8",
"rollup-plugin-typescript": "^1.0.1",
"rollup-plugin-uglify": "^6.0.3",
"tslib": "^1.10.0",
"typescript": "^3.7.2"
}
}
配置rollup
touch rollup.config.js
const path = require('path');
const buble = require('rollup-plugin-buble');
const typescript = require('rollup-plugin-typescript');
const resolveFile = function(filePath) {
return path.join(__dirname, filePath)
}
module.exports = [
{
input: resolveFile('src/main.ts'),
output: {
file: resolveFile('dist/app.js'),
format: 'cjs',
name: 'restify-demo',
},
plugins: [
typescript(),
buble(),
],
},
]
安裝mongodb
下載mongo
# 進入 /usr/local
cd /usr/local
# 下載
sudo curl -O https://fastdl.mongodb.org/osx/mongodb-osx-ssl-x86_64-4.0.9.tgz
# 解壓
sudo tar -zxvf mongodb-osx-ssl-x86_64-4.0.9.tgz
# 重命名為 mongodb 目錄
sudo mv mongodb-osx-x86_64-4.0.9/ mongodb
export PATH=/usr/local/mongodb/bin:$PATH
添加mongo.conf
# 日誌
systemLog:
# 日誌為文件
destination: file
# 文件位置
path: /usr/local/var/log/mongodb/mongo.log
# 是否追加
logAppend: true
#進程
processManagement:
# 守護進程方式
fork: true
storage:
dbPath: /usr/local/var/mongodb
net:
# 綁定IP,默認127.0.0.1,只能本機訪問
bindIp: 127.0.0.1
# 端口
port: 27017
運行mongod
mongod --config /usr/local/etc/mongo.conf
校驗mongo
> mongo
MongoDB shell version v4.0.9
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("07aaf4a0-c188-4d50-9412-96a2b0625e8a") }
MongoDB server version: 4.0.9
Server has startup warnings:
2019-11-27T17:31:59.406+0800 I CONTROL [initandlisten]
2019-11-27T17:31:59.407+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2019-11-27T17:31:59.408+0800 I CONTROL [initandlisten] **
hello-world
import restify from "restify";
import { Server } from "restify/lib/server";
function respond(req, res, next) {
res.send('hello ' + req.params.name);
next();
}
const server:Server = restify.createServer();
server.get('/hello/:name', respond);
server.head('/hello/:name', respond);
server.listen(8090, function() {
console.log('%s listening at %s', server.name, server.url);
});
hello-world請求
curl -is http://localhost:8090/hello/world -H 'accept: text/plain'
## 返回
'accept: text/plain'
HTTP/1.1 200 OK
Server: restify
Content-Type: text/plain
Content-Length: 11
Date: Wed, 20 Nov 2019 05:35:12 GMT
Connection: keep-alive
hello world
業務實踐
依賴引入
import restify from "restify";
import { Server } from "restify/lib/server";
import Mock from 'mockjs'
import { dbConnect } from "./utils";
const mongodb = require('mongodb');
const log4js = require('log4js');
const logger = log4js.getLogger();
const server:Server = restify.createServer();
//before route choose
server.pre(restify.plugins.pre.userAgentConnection());
//after router choose ,before handler
server.use(restify.plugins.acceptParser(server.acceptable));
server.use(restify.plugins.queryParser({ mapParams: false }));
server.use(restify.plugins.bodyParser());
restify 路由配置
//set route
server.post('/add',
async function(req, res, next) {
const desc = req.body.desc;
let result = await create(desc);
req.desc = {desc:'success',data:{
desc:result,
numbrer:Mock.mock({
"number|1-100": 100
})
}};
return next();
},
function(req, res, next) {
res.send(req.desc);
return next();
}
);
server.get(
'/all',
async function(req, res, next) {
let result = await fetchAll();
req.desc = result;
return next();
},
function(req, res, next) {
res.send(req.desc);
return next();
}
)
api版本支持
function sendV1(req, res, next) {
const mockNumber = Mock.mock({
"number|1-100": 100
});
res.send(`version number:${mockNumber} with param ${req.params.name}`);
return next();
}
function sendV2(req, res, next) {
const mockNumber = Mock.mock({
"number|1-100": 100
});
res.send({ param: req.params.name,version: mockNumber});
return next();
}
server.get('/version/:name', restify.plugins.conditionalHandler([
{ version: '1.1.3', handler: sendV1 },
{ version: ['2.0.0', '2.1.0', '2.2.0'], handler: sendV2 }
]));
mongo 操作
let db;
// Get a DB connection when this module is loaded
(function getDbConnection() {
dbConnect().then((database) => {
db = database;
}).catch((err) => {
logger.error('Error while initializing DB: ' + err.message, 'lists-dao-mongogb.getDbConnection()');
});
})();
function create(description) {
return new Promise((resolve, reject) => {
let lists = db.collection('shoppingLists');
let listId = mongodb.ObjectId();
let whenCreated = Date.now();
let item = {
_id: listId,
id: listId,
description: description,
whenCreated: whenCreated,
whenUpdated: null
};
lists.insertOne(item, (err, result) => {
if (err) {
logger.error('Error occurred: ' + err.message, 'create()');
reject(err);
} else {
resolve({ data: { createdId: result.insertedId }, statusCode: 201 });
}
});
});
}
function fetchAll() {
return new Promise((resolve, reject) => {
let lists = db.collection('shoppingLists');
lists.find({}).toArray((err, documents) => {
if (err) {
logger.error('Error occurred: ' + err.message, 'fetchAll()');
reject(err);
} else {
logger.debug('Raw data: ' + JSON.stringify(documents), 'fetchAll()');
resolve({ data: JSON.stringify(documents), statusCode: (documents.length > 0) ? 200 : 404 });
}
});
});
}
db連接
const mongodb = require('mongodb');
const log4js = require('log4js');
const logger = log4js.getLogger();
// logger.level = 'debug';
let mongodbClient;
let db;
const appSettings = {
mongodb_url:'mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb',
mongodb_db_name:'mongoDemo',
}
function dbClose() {
if (mongodbClient && mongodbClient.isConnected()) {
mongodbClient.close();
}
}
export function dbConnect() {
return new Promise((resolve, reject) => {
if (db) {
resolve(db);
} else {
mongodb.MongoClient.connect(appSettings.mongodb_url, function(err, client) {
if (err) {
logger.error('Error connecting to the MongoDB URL: ' + appSettings.mongodb_url);
reject(err);
}
mongodbClient = client;
db = mongodbClient.db(appSettings.mongodb_db_name);
// Make sure connection closes when Node exits
process.on('exit', (code) => {
dbClose();
})
resolve(db);
});
}
});
}
參考文獻
- restify.com/docs/home/
- mongodb.github.io/node-mongodb-native/
- www.rollupjs.com/guide/introduction/
- github.com/nuysoft/Mock/wiki/Getting-Started
- github.com/rollup/rollup-plugin-typescript
- typescript.bootcss.com/
- www.runoob.com/mongodb/mongodb-osx-install.html
- www.jianshu.com/p/ec50c1088cf1
本文作者:前端首席體驗師(CheongHu)
聯繫郵箱:[email protected]
閱讀更多 前端漫談 的文章