mongoDB配置: juejin.cn/post/720881…
技术栈:mongodBD mongoose Koa2 axios
init
npm init
package.json
{
"name": "xxx",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node bin/www",
"dev": "./node_modules/.bin/nodemon bin/www",
"prod": "pm2 start bin/www",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^0.21.0",
"debug": "^4.1.1",
"koa": "^2.7.0",
"koa-body": "^4.2.0",
"koa-bodyparser": "^4.2.1",
"koa-convert": "^1.2.0",
"koa-json": "^2.0.2",
"koa-logger": "^3.2.0",
"koa-multer": "^1.0.2",
"koa-onerror": "^4.1.0",
"koa-router": "^7.4.0",
"koa-static": "^5.0.0",
"koa-views": "^6.2.0",
"koa2-cors": "^2.0.6",
"koa2-request": "^1.0.4",
"mongodb": "^3.6.3",
"mongoose": "^5.5.11",
"pug": "^2.0.3",
"puppeteer": "^5.5.0"
},
"devDependencies": {
"nodemon": "^1.19.1"
}
}
通过npm i
下载依赖
入口文件
在我们package.json
中,启动服务需要运行bin/www
文件。
在这个文件中,需要做的事有:
- 定义port端口号
- 启一个http服务
- 监听服务异常(error)和变化
具体代码如下:
- 定义port端口号
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
var port = normalizePort(process.env.PORT || '3000');
- 启一个http服务,并监听
方式一,通过http.createServer
起一个简单的服务。
var app = require('../app');
var http = require('http');
var server = http.createServer(app.callback());
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
方式二,通过koa的app.listen起一个服务。
var opn = require('opn');
var Koa = require('koa');
var app = new Koa();
// 端口占用重启 开发环境
let startServer = function (port) {
app.listen(port, () => {
if (env === 'development') {
opn(`http://localhost:${port}`, {app: ['google chrome']});
}
}).on('error', (err) => {
console.log('端口错误+' + port);
if (env === 'development') {
startServer(PORT + 1);
}
});
}
startServer(3000)
上述两种方式底层都是通过createServer
实现的,本质没有区别。
http.createServer
做了什么? 待补充
中间件
启动服务后,需要用到koa对接口进行配置封装, 也就是上一步中,createServer的入参=>app.js文件。
在这个文件中,需要做的事有:
- 连接数据库
- 处理错误
- 解决跨域问题 / 配置响应头
- 格式化response
- 配置日志
- 限制请求体大小
- 注册路由
这里简单讲一下koa的洋葱模型:
koa中自定义方法都是通过app.use(async (ctx, next)=>{})
实现的,use中的方法是异步执行的,比如下面的代码:
为什么是这样? 待补充
const Koa = require("koa")
const app = new Koa()
app.use(async (ctx, next) => {
console.log(1)
await next;
console.log(1)
})
app.use(async (ctx, next) => {
console.log(2)
await next;
console.log(2)
})
app.use(async (ctx, next) => {
console.log(3)
})
// 最后输出
1 2 3 2 1
- 连接数据库 这里我们以mongodb为例,可以使用mongoose作为链接处理mongodb的工具。
//引入模块
const mongoose = require('mongoose')
const config = {
mongodbUrl: 'mongodb://localhost:27017/didaima',
}
//连接数据库
mongoose.connect(`${config.mongodbUrl}`, {
useNewUrlParser: true,
useUnifiedTopology: true
})
//得到数据库连接句柄
const db = mongoose.connection
//通过数据库连接句柄,监听mongoose数据库成功的事件
db.on('open', function (err) {
if (err) {
console.log('数据库连接失败')
throw err
}
console.log('数据库连接成功')
})
- 处理错误
koa-onerror
可以帮助我们于格式化异常情况的页面输出。
const onerror = require('koa-onerror')
onerror(app)
- 解决跨域问题 / 配置响应头
const cors = require('koa2-cors')
app.use(
cors(
// {
// origin: "*",
// credentials: true,
// exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
// allowMethods: ['GET', 'POST', 'DELETE'],
// allowHeaders: ['Content-Type', 'Authorization', 'Accept']
// }
)
)
- 格式化response
koa-json
可以帮我们将请求response格式化成json
var json = require("koa-json")
app.use(json())
- 配置日志
koa-logger
可以在服务端输出请求日志
var logger = require("koa-logger")
app.use(logger())
- 限制请求体大小
koa-body
可以配置对请求体的限制
var koaBody = require("koa-body")
app.use(
koaBody({
formLimit: '15mb',
jsonLimit: '15mb',
textLimit: '15mb'
})
)
- 注册路由
这一步比较重要,我们的接口都将在这边实现。
const Router = require('koa-router')
const router = new Router()
const registerRouter = require('./routes')
app.use(registerRouter())
app.use(router.routes()) // 中间件中使用router
app.use(router.allowedMethods());
由于我们可能不止一个router文件,所以在这里构造一个统一的注册器,通过node读取router文件夹下的所有router文件,并依次注册。
routes.js
const compose = require('koa-compose')
const glob = require('glob')
const { resolve } = require('path')
registerRouter = () => {
let routers = [];
glob.sync(resolve(__dirname, './', '**/*.js'))
.filter(value => (value.indexOf('index.js') === -1))
.map(router => {
routers.push(require(router).routes())
routers.push(require(router).allowedMethods())
})
return compose(routers)
}
module.exports = registerRouter
koa-compose
是将 koa/koa-router 各个中间件合并执行,也就是依次注册路由。
至此,一个可以链接mongodb数据库的koa中间件服务就起好了,试试npm run node 入口文件
mongoose
这里我们细说一下mongoose。
mongoose 是 MongoDB 的一个对象模型工具,它对 MongoDB 的常用方法进行了封装,让 node.js 操作 MongoDB 更加优雅简洁。
数据库操作中无非就是CRUD(增删改查),mongoose通过connect
方法连接数据库后,同样可以调用方法来控制CRUD。
增 - insert
虽然mongoDB不需要在创建文档的时候指定文档的数据结构,但是mongoose中推荐使用model去定义一下schema。
比如,我们要创建一个描述图书馆中的图书的文档:
/* 定义 user Schema */
const mongoose = require("mongoose");
const bookSchema = new mongoose.Schema({
bookName: { type: String },
currentCount: { type: Number }
});
// 创建Model
const PageMedel = mongoose.model("book", bookSchema, 'book');
module.exports = PageMedel
通过注册路由去操作增加(insert)数据:
model可以通过insertMany()
去新增数据。
const router = new Router()
const Router = require('koa-router')
const BookModel = require('../models/book')
router.post('/addBook', async (ctx, next) => {
let data = ctx.request.body
let book = BookModel;
try {
page = await page.insertMany([data])
ctx.body = {
message: '保存成功',
data: page[0]._id,
code: 0
}
} catch (e) {
ctx.body = { message: `insert失败, 失败原因:${e}`, code: 1 }
}
await next()
})
完成上述工作,就可以调用/addBook
接口去新增图书了~
删
删除可以使用model.remove
model.remove({ _id: data.id }, (err, d) => {
if (err) {
reject({ message: '删除失败', status: 10001 })
} else {
resolve({ message: '删除成功', status: 10000, id: data.id })
}
})
改
moongoose中的修改和mongoDB中原生的修改方法类似,这里列举一个updateOne
{ _id: data.id }
是过滤器的过滤条件。
$set
是指部分属性更新,而不是覆盖。
model.updateOne({ _id: data.id }, {$set: data}, (err, d) => {
if (err) {
reject({ message: '编辑失败', status: 10001 })
} else {
resolve({ message: '编辑成功', status: 10000, id: data.id })
}
})
查
可以使用以下方法做查询
- model.find({})
- model.findOne({ bookName: "js从入门到放弃" })
- model.findById(id)
- ...