如下是一个单独拆分出来处理公告板数据(表)的路由模块,为了逻辑清晰,所有处理请求的函数也都独立放在各自的js中。
import express from 'express'
const router = express.Router()
import board from './board'
import boardAdd from './board-add'
import boardOne from './board-info'
import boardDelete from './board-delete'
//列表路由
router.get('/', board)
//添加功能路由
router.post('/', boardAdd)
//查询功能路由
router.get('/:id', boardOne)
//编辑功能路由
router.put('/:id', boardAdd)
//删除功能路由
router.delete('/:id', boardDelete)
export default router
在一个项目中我们会有很多这样的数据模块,而且请求路由来来去去也就是这几项:获取列表、查询、删除、添加等。
即使模块(Model)不同、处理这些路由的每一项功能其实都大同小异,尤其是只有传递了params(通常是id),没有请求体的时候。那我们就可以把这些简单的逻辑封装一下,以后改需求可以省去很多代码量。
来看实际代码吧。
1. 删除数据
以下就是封装好的数据删除功能的js。原本我写成最终返回一个Promise,调用时再用async/await拿返回值,后来觉得有点繁琐就全都改成直接把路由参数 (req, res, next) 传过去了。(注:这里操作数据库用的是MongoDB的mongoose)
// routes/common/delete.js
/*
routerParams: Object {req, res, next} 路由中间件回调函数的固定参数
Model: 数据模型
*/
export default async (routerParams, Model) => {
const { req, res, next } = routerParams
let id = req.params.id
if (id.indexOf(',') > 0) { // 批量删除
id = id.split(',')
try {
const response = await Model.deleteMany({ _id: { $in: id } }).exec()
if(response.ok && response.deletedCount) {
res.json({
code: 200,
message: '批量删除成功'
})
} else{
res.json({
code: 400,
message: '删除失败'
})
}
} catch (err) {
next(err)
}
} else {
try {
const response = await Model.deleteOne({ _id: { $in: id } }).exec()
if(response.ok && response.deletedCount) {
res.json({
code: 200,
message: '批量删除成功'
})
} else{
res.json({
code: 400,
message: '删除失败'
})
}
} catch (err) {
next(err)
}
}
}
在处理delete
路由的函数里引入直接使用即可
// board-delete.js
import Board from '../../../../model/board' // Board模型
import deleteById from '../../common/delete' // 操作数据删除
export default (req, res, next) => {
deleteById({ req, res, next }, Board)
}
2. 根据id查询数据
// routes/common/getOne.js
export default async (routerParams, Model, ref) => {
const { req, res, next } = routerParams
const id = req.params.id
const refKey = ref ? ref : ''
try {
const doc = await Model.findById(id).populate(refKey).exec()
res.json({
code: 200,
message: '查询成功',
data: doc
})
} catch (err) {
// 如果想中处理错误,也可以 next(err) 交给最后的中间件
// next(err)
res.json( {
code: 400,
message: '查询失败' + err.message
})
}
}
查询路由处理函数
// board-info.js
import Board from '../../../../model/board'
import getById from '../../common/getOne'
export default (req, res, next) => {
// 如果需要联表查询,最后一个参数传入Model中关联表的字段名即可
getById({ req, res, next }, Board, 'author')
}
参考 Board 模型(关联字段 author )
const boardSchema = new mongoose.Schema({
title: {
type: String,
maxlength: 30,
minlength: 8,
required: [true, '文章标题不可为空'],
},
content: { // 内容
type: String,
required: [true, '内容不可为空']
},
author: { // 关联User表的字段名
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: [true, '请传递作者']
}
......
}
const Board = mongoose.model('Boarde', boardSchema)
3. 分页查询
// routes/common/getList.js
// 分页查询
export default (props) => {
const {
req,
res,
next,
page,
pageSize,
condition,
sortCondition,
Model,
ref
} = props
const limit = parseInt(pageSize)
const skip = (page - 1) * limit
const refKey = ref ? ref : ''
Model.countDocuments(condition).exec()
.then(response => {
return response
})
.then(total => {
const message = total ? '列表查询成功' : '未查询到符合条件的数据'
Model.find(condition).skip(skip).limit(limit).populate(refKey).sort(sortCondition).collation({
locale: 'zh'
}).exec().then(response => {
if(response) res.json({
code: 200,
message,
data: {
list: response,
total
}
})
})
})
.catch (err => {
res.json({
code: 400,
message: '列表查询失败' + err.message
})
})
}
请求分页列表的路由逻辑我们只关注传参即可。
// board-list.js
import Board from '../../../../model/board'
import getList from '../../common/getList'
export default (req, res, next) => {
const {
page,
pageSize,
query,
exchangeType,
user,
sort
} = req.query
const nameReg = new RegExp(query.trim(), 'i')
let condition = {
"detail": nameReg
}
// 其他筛选条件处理和添加,筛选参数比较多的情况会比较方便
const filterList = [{user}, {exchangeType}]
filterList.forEach(item => {
if(item) {
const ckey = Object.keys(item)[0]
const cVal = Object.values(item)[0]
if(cVal){
condition[ckey] = { $in: cVal}
}
}
})
let sortCondition = {
validTime: 1
}
if (sort) sortCondition = JSON.parse(sort)
getList({
req,
res,
next,
page,
pageSize,
condition,
sortCondition,
Model: Board,
ref: 'author'
})
}
4. 新增/修改数据
// routes/common/add.js
// 通用款:没太多特殊需求的新增 /修改
/**
* routerParams: Object {req, res, next} 路由中间件回调函数的固定参数
* Model 表模型名
* props: Object 其它参数
* - key: Array, 新增数据不能重复值的键名数组,默认是['name'] / 若值为nonUnique则表示无需查重
* - addTime: Boolean, 是否添加创建时间
* - uniqueName: String, 查重处理返回的重复字段名称
*/
export default async (routerParams, Model, props) => {
const {req, res, next} = routerParams
const data = req.body
const options = props ? props : {}
const { key, addTime, uniqueName } = options
const uniqueKey = !key ? ( key === false ? false : ['name'] ) : key
const uniqueText = uniqueName ? uniqueName : '名称'
const addItem = async () => {
if (addTime) data.created_time = Date.parse(new Date()) / 1000
try {
await Model.create(data)
res.json({
code: 200,
message: '添加成功'
})
} catch(err) {
res.json({
code: 400,
message: '添加失败'
})
}
}
if (data._id) { // 修改数据
try {
await Model.findByIdAndUpdate(data._id, data).exec()
res.json({
code: 200,
message: '修改成功'
})
} catch (err) {
res.json({
code: 400,
message: err
})
}
} else { // 新增数据
if (uniqueKey) { // 查重处理
let condition = {}
uniqueKey.forEach(item => condition[item] = data[item])
const doc = await Model.findOne(condition)
if(doc) {
res.json({
code: 400,
message: `该${uniqueText}已存在,请勿重复添加`
})
} else {
addItem()
}
} else {
addItem()
}
}
}
// guide-add.js
import Guide from '../../../../model/guide'
import AddData from '../../common/add'
export default (req, res, next) => {
if (req.body.author) req.body.author = req.body.author._id
AddData({ req, res, next }, Guide, { key: ['title'], addTime: true, uniqueName: '标题' })
}
5. 数据查询(不分页)
// routes/common/search.js
// 查询所有符合条件的数据,不分页
export default async (routerParams, Model, key) => {
const { req, res, next } = routerParams
const queryKey = key ? key : 'name'
const queryVal = req.query[queryKey]
const keyReg = new RegExp(queryVal.trim(), 'i')
try {
const response = await Model.find({
[queryKey]: {
$regex: keyReg
}
}).exec()
res.json({
code: 200,
message: '查询成功',
data: response
})
} catch (err) {
res.json({
code: 400,
message: '查询失败' + err.message
})
}
}
// user-search.js
// 实时搜索用户名字
import User from '../../../../model/user'
import searchAll from '../../common/search'
export default (req, res, next) => {
searchAll({ req, res, next }, User, 'username')
}
现在路由处理函数的代码已经非常简洁了,那我们直接放到路由模块里去也可,完全不影响条理的明晰。看个人习惯吧。
像下面就是一个改造后的路由模块,清爽到飞起~
import express from 'express'
const router = express.Router()
// 数据 Model
import Furniture from '../../../../model/furniture'
// 增删改查等共用处理程序
import furnitureList from './furnitureList'
import AddData from '../../common/add'
import getById from '../../common/getOne'
import deleteById from '../../common/delete'
import searchAll from '../../common/search'
// 分页列表路由,代码相对较长,单独放一个js再引入
router.get('/', furnitureList)
// 实时搜索全部符合条件的家具
router.get('/search', (req, res, next) => {
searchAll({ req, res, next }, Furniture)
})
// 添加功能路由
router.post('/', (req, res, next) => {
AddData({ req, res, next }, Furniture)
})
// 查询功能路由
router.get('/:id', (req, res, next) => {
getById({ req, res, next }, Furniture)
})
// 删除功能路由
router.delete('/:id', (req, res, next) => {
deleteById({ req, res, next }, Furniture)
})
export default router
网友评论