需求
接口地址 http://localhost:3000/api/{model}/{param}
根据不同的model和参数param,实现对不同接口进行增删改查操作
例1:
api = http://localhost:3000/api/user; method = [get, post]
api = http://localhost:3000/api/user/5e16e428f89752a788a555fd; method = [put, delete]
例2:
api = http://localhost:3000/api/article; method = [get, post]
api = http://localhost:3000/api/article/5e16f37d03cf1ea898989bb2; method = [put, delete]
目录结构
restful
│ conf.js
│ index.js
│
├─framework
│ api.js
│ loader.js
│ router.js
│
└─model
article.js
user.js
一、数据库参数配置
- conf.js
module.exports = {
db: {
url: 'mongodb://localhost:27017/test_restful',
options: {
userNewUrlParser: true
}
}
}
二、编写数据模型
- user.js
module.exports = {
schema: {
mobile: {
type: String,
required: true
},
realName: {
type: String,
required: true
}
}
}
- article.js
module.exports = {
schema: {
title: {
type: String,
required: true
},
author: {
type: String,
required: true
}
}
}
三、编译数据模型model,创建数据库连接
- loader.js
const fs = require('fs')
const path = require('path')
const mongoose = require('mongoose')
// 加载文件下的model,回调
function load(dir, cb) {
// 获取绝对路径
const url = path.resolve(__dirname, dir)
// 获取model文件,files为数组
const files = fs.readdirSync(url)
files.forEach(filename => {
// 去掉后缀
filename = filename.replace('.js', '')
// 导入文件
const file = require(url + '/' + filename)
// 回调处理逻辑(文件名,文件)
cb(filename, file)
})
}
// 函数柯里化(高价函数)
// 加载编译model
const loadModel = config => app => {
// 连接数据库
mongoose.connect(config.db.url, config.db.options)
const conn = mongoose.connection
conn.on('error', () => { console.log('数据库连接失败') })
// 在app上挂载全局model
app.$model = {}
// 加载数据表路径下的文件
load('../model', (filename, { schema }) => {
// 将schema编译成一个Model
app.$model[filename] = mongoose.model(filename, schema)
})
}
module.exports = {
loadModel
}
四、路由导航
- router.js
- init方法拦截实现动态路由
const router = require('koa-router')()
const { init, get, create, update, del } = require('./api')
router.get('/api/:model', init, get)
router.post('/api/:model', init, create)
router.put('/api/:model/:id', init, update)
router.delete('/api/:model/:id', init, del)
module.exports = router.routes()
五、路由拦截,接口实现
- api.js
module.exports = {
/**
* ctx.params.model参数会在model/文件夹下文件名匹配
* 若文件存在,则取对该文件进行执行接口的操作;否则返回‘no this model’
* 例;ctx.params.model = 'user'或'article'时;
* 则当前路由导航的地址就为'http://localhost:3000/api/user' 或 'http://localhost:3000/api/article'
*/
async init(ctx, next) {
const model = ctx.app.$model[ctx.params.model]
if (model) {
ctx.model = model
await next()
} else {
ctx.body = 'no this model'
}
},
// 查询
async get(ctx) {
ctx.body = await ctx.model.find({})
},
// 新增
async create(ctx) {
const res = await ctx.model.create(ctx.request.body)
ctx.body = res
},
// 修改
async update(ctx) {
const res = await ctx.model.updateOne({_id: ctx.params.id}, ctx.request.body)
ctx.body = res
},
// 删除
async del(ctx) {
const res = await ctx.model.deleteOne({ _id: ctx.params.id })
ctx.body = res
}
}
六、入口文件
- index.js
const Koa = require('koa')
const app = new Koa()
// 初始化数据库
const config = require('./conf')
const { loadModel } = require('./framework/loader')
loadModel(config)(app)
const restful = require('./framework/router')
const bodyParser = require('koa-bodyparser')
app.use(bodyParser())
app.use(restful)
const port = 3000
app.listen(port, () => {
console.log(`app start at port ${port}...`)
})
7、执行结果
- http://localhost:3000/api/user/
- http://localhost:3000/api/user/
- http://localhost:3000/api/new