前言
寫這個(gè)的初衷是由于現(xiàn)在的記事本app都太冗余了徙瓶,我很喜歡google的keep,但要是想同步的話還要翻墻嫉称;有道云筆記又有些大侦镇,ui也不夠直觀,太辦公向了织阅,寫博客我很喜歡壳繁,但隨手寫點(diǎn)備忘錄,還是顯得有點(diǎn)重荔棉。正好我使用react native也有一段時(shí)間了闹炉,學(xué)node一直也找不到自己喜歡的項(xiàng)目練手,不如就自己寫一個(gè)吧~
在寫這個(gè)項(xiàng)目之前润樱,可以說一點(diǎn)都不了解mongoDB渣触,不了解他的數(shù)據(jù)結(jié)構(gòu);可以說是一個(gè)完全的后端小白了壹若。嗅钻。
如果對(duì)這個(gè)項(xiàng)目有興趣的朋友可以在github上點(diǎn)下star,本人也會(huì)一直更新迭代~有疑問和建議的朋友可以在評(píng)論區(qū)指出 ~嘻嘻
起步
先建個(gè)服務(wù)
// server.js
const express=require('express')
const app=express()
// 用于上傳筆記
app.get('/upload',(req,res)=>{
res.send('uploaded!!')
})
const server=app.listen(8888,()=>{
const host=server.address().address
const port=server.address().port
console.log(`the server is listening on ${host}:${port}`)
})
初始化路由
基本上有三個(gè)路由店展,上傳养篓,刪除,查找
// server.js
// 上傳文章
app.get('/upload', (req, res) => {
res.send('uploaded!!');
});
// 刪除文章
app.get('/delete', (req, res) => {
res.send('delete!!');
});
// 按分類查找
app.get('/findByLabel', (req, res) => {
res.send('findByLabel!!');
});
初始化數(shù)據(jù)庫
數(shù)據(jù)庫使用mongodb壁查,我用的是node-mongodb-native作為引擎他是mongodb官方的node引擎,express官網(wǎng)上推薦的mongoskin剔应,有一年多沒更新了就放棄了睡腿。
新建一個(gè)數(shù)據(jù)庫配置文件,
const { MongoClient } = require('mongodb');
const assert = require('assert');
// Connection URL
const url = 'mongodb://localhost:27017';
// Database Name
const dbName = 'memo';
function connectDB() {
// Use connect method to connect to the server
MongoClient.connect(url, (err, client) => {
assert.equal(null, err);
console.log('Connected successfully to server');
const db = client.db(dbName);
client.close();
});
}
module.exports = { connectDB };
在server.js中引用這個(gè)模塊,啟動(dòng)服務(wù)后峻贮,終端打印了Connected successfully to server
席怪,即為連接成功。
自定義配置
將配置信息單獨(dú)放在一個(gè)文件夾中纤控,方便維護(hù)挂捻。這里寫入了監(jiān)聽端口和數(shù)據(jù)庫信息,如果有其他的船万,將在后面加入刻撒。
module.exports = {
port: 8888,
mongodb: 'mongodb://localhost:27017',
dbName: 'memo',
};
使用promise重構(gòu)連接數(shù)據(jù)庫方法
查看了文檔,因?yàn)槠渌僮鲾?shù)據(jù)庫的api都依賴于MongoClient.connect
方法中的回調(diào)函數(shù)中的參數(shù)client
耿导。因?yàn)椴僮鲾?shù)據(jù)庫復(fù)雜声怔,為了使代碼更清晰,同時(shí)避免回調(diào)地獄舱呻,使用promise做個(gè)封裝醋火,進(jìn)行鏈?zhǔn)秸{(diào)用。
// mongo.js
const connectDB = new Promise((resolve, reject) => {
MongoClient.connect(mongodbURL, (err, client) => {
if (err) {
reject(err);
} else {
assert.equal(null, err);
console.log('Connected successfully to server');
const db = client.db(dbName);
resolve(db);
}
});
});
module.exports = { connectDB };
調(diào)用:
connectDB.then((db) => {
findDocuments(db);
});
編寫添加方法
基礎(chǔ)模板
const assert = require('assert');
const insertDocuments = function (db, callback = () => {}) {
// Get the documents collection
const collection = db.collection('documents');
// Insert some documents
collection.insertOne({ a: 'test' }, (err, result) => {
assert.equal(err, null);
assert.equal(1, result.result.n);
assert.equal(1, result.ops.length);
console.log('Inserted a document into the collection');
callback(result);
});
};
module.exports = { insertDocuments };
其他的操作,如刪除或查找芥驳,都可以在官方文檔上都可以找到柿冲,就不一一舉例了。
創(chuàng)建錯(cuò)誤實(shí)例
包括一個(gè)請(qǐng)求狀態(tài)碼和錯(cuò)誤碼
// createError.js
function createError(code = 1) {
return {
status: 1,// 1為錯(cuò)誤兆旬;0為成功
code,
};
}
module.exports = { createError };
我們?cè)倬帉憥讉€(gè)可能會(huì)出現(xiàn)的錯(cuò)誤實(shí)例假抄,如:
// error.js
const errors = {
CONNECT_DB_FAILED: 'CONNECT_DB_FAILED', // 連接數(shù)據(jù)庫失敗
};
稍微更改下connectDB的邏輯:
const connectDB = new Promise((resolve, reject) => {
MongoClient.connect(mongodbURL, (err, client) => {
if (err) {
reject(createError(errors.CONNECT_DB_FAILED));
} else {
const db = client.db(dbName);
resolve(db);
}
});
});
嘗試把config.js中的mongodbURL更改為錯(cuò)誤的地址,查看終端是否打印{ status: 1, code: 'CONNECT_DB_FAILED' }
爵憎。
用promise封裝添加方法
由于官方給出的代碼例子都是回調(diào)形式慨亲,代碼意圖不清晰,使用promise封裝宝鼓,避免回調(diào)地獄刑棵。
const insertDocuments = (db, data = { test: 1 }, collectionName = 'documents') => new Promise((resolve, reject) => {
const collection = db.collection(collectionName);
collection.insertOne(data, (err, { result }) => {
if (err) {
reject(createError(errors.HANDLE_DB_FAILED));
} else {
resolve(createResult(result));
}
});
});
在index.js中,更改調(diào)用方式愚铡,終端會(huì)打印data { status: 0, body: { n: 1, ok: 1 } }
蛉签。
connectDB
.then(db => insertDocuments(db))
.then(data => console.log('data', data))
.catch((err) => {
console.log(err);
});
解析請(qǐng)求
我們發(fā)送的請(qǐng)求中的數(shù)據(jù),要放到req.body中去接收沥寥,這里需要安裝express的中間件bodyParser
const bodyParser = require('body-parser');
app.use(bodyParser.json({ extended: true }));
使用postman測(cè)試一下碍舍,發(fā)送請(qǐng)求{{URL}}/upload
,body中寫入
{
"context":"hello world"
}
我們編寫一下upload函數(shù)邑雅,來打印一下
function upload(req, res) {
const { context } = req.body;
console.log(context) // hello world
res.send('uploaded!!');
}
控制臺(tái)有輸出片橡,則成功。
之后淮野,把之前寫好的上傳函數(shù)寫入upload中捧书。
function upload(req, res) {
const { context } = req.body;
connectDB
.then(db => insertDocuments(db, context))
.then(data => console.log('data', data))
.catch((err) => {
console.log(err);
});
res.send('uploaded!!');
}
執(zhí)行正常,則會(huì)返回{ status: 0, body: { n: 1, ok: 1 } }
錯(cuò)誤實(shí)例分類
在請(qǐng)求的過程中骤星,可能會(huì)發(fā)生網(wǎng)絡(luò)錯(cuò)誤经瓷、數(shù)據(jù)庫錯(cuò)誤或是接口參數(shù)錯(cuò)誤;如果單純的返回一個(gè)錯(cuò)誤狀態(tài)洞难,整個(gè)服務(wù)就顯得太不可靠了舆吮。
在constants文件夾下創(chuàng)建一個(gè)錯(cuò)誤常量的對(duì)象,在后續(xù)的開發(fā)過程中我們將加入更多的錯(cuò)誤類型队贱,先簡(jiǎn)單寫這幾個(gè)
const errors = {
CONNECT_DB_FAILED: 'CONNECT_DB_FAILED', // 連接數(shù)據(jù)庫失敗
HANDLE_DB_FAILED: 'HANDLE_DB_FAILED', // 與數(shù)據(jù)庫操作時(shí)發(fā)生錯(cuò)誤
INVALID_PARAMETERS: 'INVALID_PARAMETERS', // 參數(shù)錯(cuò)誤
SYSTEM_ERROR: 'SYSTEM_ERROR', // 系統(tǒng)錯(cuò)誤色冀,可能是語法,查看日志
};
module.exports = errors;
然后柱嫌,我們?cè)賱?chuàng)建幾個(gè)錯(cuò)誤處理函數(shù)呐伞,用于把錯(cuò)誤類型傳遞到響應(yīng)參數(shù)中。如下:
function parameterInvalid(res) {
res.json(createError(INVALID_PARAMETERS));
}
function unknowError(err, res) {
console.log(err);
res.json(createError(SYSTEM_ERROR));
}
module.exports = { parameterInvalid, unknowError };
把上面的函數(shù)寫進(jìn)upload方法中:
const { parameterInvalid, unknowError } = require('../lib/handleErrors');
function upload(req, res) {
const { context } = req.body;
if (!context) {
parameterInvalid(res);
return;
}
connectDB
.then(db => insertDocuments(db, { context }))
.then(data => res.json(data))
.catch((err) => { unknowError(err, res); });
}
以上慎式,一個(gè)基礎(chǔ)的上傳服務(wù)就完成了~~ 還有其他的操作伶氢,如刪除趟径、更新等,其實(shí)都大同小異癣防,區(qū)別于node-mongo的api蜗巧,大家可以查閱官方文檔或是查看項(xiàng)目源代碼~