前言
写这个的初衷是由于现在的记事本app都太冗余了,我很喜欢google的keep,但要是想同步的话还要翻墙;有道云笔记又有些大,ui也不够直观,太办公向了,写博客我很喜欢,但随手写点备忘录,还是显得有点重。正好我使用react native也有一段时间了,学node一直也找不到自己喜欢的项目练手,不如就自己写一个吧~
在写这个项目之前,可以说一点都不了解mongoDB,不了解他的数据结构;可以说是一个完全的后端小白了。。
如果对这个项目有兴趣的朋友可以在github上点下star,本人也会一直更新迭代~有疑问和建议的朋友可以在评论区指出 ~嘻嘻
起步
先建个服务
// 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}`)
})
初始化路由
基本上有三个路由,上传,删除,查找
// 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!!');
});
初始化数据库
数据库使用mongodb,我用的是node-mongodb-native作为引擎他是mongodb官方的node引擎,express官网上推荐的mongoskin,有一年多没更新了就放弃了。
新建一个数据库配置文件,
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中引用这个模块,启动服务后,终端打印了Connected successfully to server
,即为连接成功。
自定义配置
将配置信息单独放在一个文件夹中,方便维护。这里写入了监听端口和数据库信息,如果有其他的,将在后面加入。
module.exports = {
port: 8888,
mongodb: 'mongodb://localhost:27017',
dbName: 'memo',
};
使用promise重构连接数据库方法
查看了文档,因为其他操作数据库的api都依赖于MongoClient.connect
方法中的回调函数中的参数client
。因为操作数据库复杂,为了使代码更清晰,同时避免回调地狱,使用promise做个封装,进行链式调用。
// 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 };
调用:
connectDB.then((db) => {
findDocuments(db);
});
编写添加方法
基础模板
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 };
其他的操作,如删除或查找,都可以在官方文档上都可以找到,就不一一举例了。
创建错误实例
包括一个请求状态码和错误码
// createError.js
function createError(code = 1) {
return {
status: 1,// 1为错误;0为成功
code,
};
}
module.exports = { createError };
我们再编写几个可能会出现的错误实例,如:
// error.js
const errors = {
CONNECT_DB_FAILED: 'CONNECT_DB_FAILED', // 连接数据库失败
};
稍微更改下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更改为错误的地址,查看终端是否打印{ status: 1, code: 'CONNECT_DB_FAILED' }
。
用promise封装添加方法
由于官方给出的代码例子都是回调形式,代码意图不清晰,使用promise封装,避免回调地狱。
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中,更改调用方式,终端会打印data { status: 0, body: { n: 1, ok: 1 } }
。
connectDB
.then(db => insertDocuments(db))
.then(data => console.log('data', data))
.catch((err) => {
console.log(err);
});
解析请求
我们发送的请求中的数据,要放到req.body中去接收,这里需要安装express的中间件bodyParser
const bodyParser = require('body-parser');
app.use(bodyParser.json({ extended: true }));
使用postman测试一下,发送请求{{URL}}/upload
,body中写入
{
"context":"hello world"
}
我们编写一下upload函数,来打印一下
function upload(req, res) {
const { context } = req.body;
console.log(context) // hello world
res.send('uploaded!!');
}
控制台有输出,则成功。
之后,把之前写好的上传函数写入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!!');
}
执行正常,则会返回{ status: 0, body: { n: 1, ok: 1 } }
错误实例分类
在请求的过程中,可能会发生网络错误、数据库错误或是接口参数错误;如果单纯的返回一个错误状态,整个服务就显得太不可靠了。
在constants文件夹下创建一个错误常量的对象,在后续的开发过程中我们将加入更多的错误类型,先简单写这几个
const errors = {
CONNECT_DB_FAILED: 'CONNECT_DB_FAILED', // 连接数据库失败
HANDLE_DB_FAILED: 'HANDLE_DB_FAILED', // 与数据库操作时发生错误
INVALID_PARAMETERS: 'INVALID_PARAMETERS', // 参数错误
SYSTEM_ERROR: 'SYSTEM_ERROR', // 系统错误,可能是语法,查看日志
};
module.exports = errors;
然后,我们再创建几个错误处理函数,用于把错误类型传递到响应参数中。如下:
function parameterInvalid(res) {
res.json(createError(INVALID_PARAMETERS));
}
function unknowError(err, res) {
console.log(err);
res.json(createError(SYSTEM_ERROR));
}
module.exports = { parameterInvalid, unknowError };
把上面的函数写进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); });
}
以上,一个基础的上传服务就完成了~~ 还有其他的操作,如删除、更新等,其实都大同小异,区别于node-mongo的api,大家可以查阅官方文档或是查看项目源代码~