express+mongoose 实现简易后台数据接口

之前刚入门vue并做好了一个简而全的纯vue2全家桶的项目,数据都是本地 json 模拟请求的;详情请移步这里:vue-proj-demo

为了真正做到数据库的真实存取,于是又开始入门了 node+express+mongoose 、并以此来为之前的vue页面写后台数据接口。

本文涉及的源码: vue-node-proj

基本数据模型 schema

以下涉及到mongodb操作的前提,是要配置好mongodb环境的;
mongodb 的安装配置、mongoose 的基本使用,请参考 http://gjincai.github.io/categories/mongodb/

mongodb 主要建了3个数据模型:

  1. 用户信息数据结构:主要包含:用户名(手机)、密码、注册时间
  2. 商品信息数据结构:主要是该商品的信息,brand_id是唯一标识、brand_cate是商品分类(男装、女装…)
  3. 购物车信息数据结构:购物车商品的大部分字段跟商品信息相同;主要通过name字段将用户该用户购物车信息联系起来;cart_num、cart_isSelect分别为该商品购物车中的数量、是否打钩选中状态。
    详情如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 用户信息的数据结构模型
const userSchema = new Schema({
name: {type: String},
pwd: {type: String},
time: {type: Date, default: Date.now}
})
// 商品的数据结构模型
const goodsSchema = new Schema({
brand_id: Number,
brand_cate: String,
brand_cateName: String,
brand_status: String,
brand_name: String,
brand_price: Number,
brand_desc: String,
brand_pic: String
})
// 购物车的的数据结构模型
const cartsSchema = new Schema({
name: String,
brand_id: Number,
brand_cate: String,
brand_name: String,
brand_price: Number,
brand_desc: String,
brand_pic: String,
cart_num: Number,
cart_isSelect: Boolean
})

连接数据库

开始连接数据库,当数据库 test_nodeVue 不存在时,会自动创建以此为名字的mongodb数据库。

1
2
3
4
5
6
7
8
9
10
const database = mongoose.connect('mongodb://127.0.0.1:27017/test_nodeVue')
database.connection.on('error', function(error){
console.log('数据库test_nodeVue连接失败:' + error)
return
})
database.connection.once('open', function(){
console.log('数据库test_nodeVue连接成功')
// 初始化数据库
initData();
})

初始化数据库

连接数据库,当首次连接的时候、数据库还是空的;
当成功连接上数据库的时候,先查询数据库是否为空;若空则往数据库里插入初始化的商品数据(initGoods.json)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const initData = function () {
// 初始化商品goods
db.goodsModel.find({}, function(err, doc){
if (err) {
console.log('initData出错:' + err);
} else if (!doc.length) {
console.log('db goodsModel open first time');
// 初始化数据,遍历插入
initGoods.map(brand => {
db.goodsModel.create(brand)
})
// console.log(initGoods)
} else {
console.log('db open not first time');
}
})
}

数据的更新 update

mongoose 数据增删查改的基本操作,详情参考:源码
这里主要说一下 update:
参数:testModel.update(conditionsObj, updateObj, upsert, function(){})
conditionsObj:查询条件
updateObj:需要更新的内容
upsert:当conditionsObj存在,则更新;不存在,则以conditionsObj、upsert为基础创建

eg:加入购物车的时候,需要根据用户名商品id判断该商品是否已经存在于用户的购物车里面;若存在,则更新;不存在,则新建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// api addToCart
app.get('/api/goods/addToCart', function (req, res) {
let brand_id = req.query.brand_id
let name = req.query.name
let newCart = req.query
db.cartsModel.update({brand_id: brand_id, name: name}, {$set:newCart}, {upsert:true}, function(err){
if (err) {
console.log('加入购物车失败:' + err);
res.json({code: 700, msg:'加入购物车失败:' + err})
return
} else {
// add
res.json({code: 200, msg:'加入购物车成功'})
return
}
})
})

vue-cli 跨域请求配置

前端vue项目的开发环境dev地址: localhost:8080,
后台node服务器的访问地址是: 127.0.0.1:8889,
(本地开启的mongodb服务的地址是: 127.0.0.1:27017)

当前端与后台进行数据交互时,自然就出现跨域问题。
通过在前端修改 vue-cli 的配置可解决:
vue-cli中的 client/config/index.js 下配置 dev选项的 {proxyTable}:

1
2
3
4
5
6
7
8
9
10
proxyTable: {
// proxy all requests starting with /api to jsonplaceholder
'/api': {
target: 'http://127.0.0.1:8889/api',
changeOrigin: true,
pathRewrite: {
'^/api': '' // 若target中没有/api、这里又为空,则404;
}
}
}

然后,eg 在请求商品详情时:

1
2
3
4
5
6
7
8
9
10
this.$http({
url: '/api/goods/detail',
method: 'GET',
params: {
brand_id: this.$route.params.id
}
})
.then((res) => {
// doSomething
})

这里的请求url /api/goods/detail、对应的真实请求地址是 http://127.0.0.1:8889/api/goods/detail

proxy的官网文档: https://vuejs-templates.github.io/webpack/proxy.html

promise 在mongodb中的异步查询

在写首页 api 接口时,需要在mongodb查询出三组数据,全部查询出来后、再返回前端。

嵌套回调查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let data1,data2,data3
db.goodsModel.find({}, function(err, doc){
if (err) {
console.log('err')
} else {
data1 = doc
db.goodsModel.find({}, function(err, doc){
if (err) {
console.log('err')
} else {
data2 = doc
db.goodsModel.find({}, function(err, doc){
if (err) {
console.log('err')
} else {
data3 = doc
// ......无穷回调
}
})
}
})
}
})

异步查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
let data1,data2,data3
const p1 = new Promise((resolve,reject) => {
db.goodsModel.find({}, function(err, doc){
if (err) {
console.log('err')
reject('reject p1')
} else {
data1 = doc
resolve(data1)
}
})
}
const p2 = new Promise((resolve,reject) => {
db.goodsModel.find({}, function(err, doc){
if (err) {
console.log('err')
reject('reject p2')
} else {
data2 = doc
resolve(data2)
}
})
}
const p3 = new Promise((resolve,reject) => {
db.goodsModel.find({}, function(err, doc){
if (err) {
console.log('err')
reject('reject p3')
} else {
data3 = doc
resolve(data3)
}
})
}
const p_all = Promise.all([p1, p2, p3])
p_all.then( (suc) => {
let data = {
"temai": suc[0],
"rexiao": suc[1],
"jingpin": suc[2]
}
}).catch( (err) => {
console.log('err all:' + err)
})