MERN栈Mongoose多查询路由冲突:findById与findByUser调用异常
React-Big-Calendar 后端路由冲突问题:findById与findByUser无法同时正常工作
我正在使用react-big-calendar,需要实现两个核心功能:
findById:用于编辑和删除单个事件findByUser:实现数据库持久化,按用户ID查询该用户的所有事件
控制器代码
const db = require("../models"); module.exports = { findAll: function(req, res) { db.Event .find() .then(dbModel => { res.json(dbModel) }) .catch(err => res.status(422).json(err)); }, findByUser: function(req, res) { db.Event .find({user: req.params.user}) .then(dbModel => res.json(dbModel)) .catch(err => res.status(422).json(err)); }, findById: function(req, res) { db.Event .findById(req.params.id) .then(dbModel => res.json(dbModel)) .catch(err => res.status(422).json(err)); }, create: function(req, res) { db.Event .create(req.body) .then(dbModel => res.json(dbModel)) .catch(err => res.status(422).json(err)); }, update: function(req, res) { db.Event .findOneAndUpdate( { "_id": req.params.id }, { "title": req.body.title, "start": req.body.start, "end": req.body.end, "description": req.body.description }, { new: true } ) .then(dbModel => res.json(dbModel)) .catch(err => res.status(422).json(err)) }, remove: function(req, res) { db.Event .findById({ _id: req.params.id }) .then(dbModel => dbModel.remove()) .then(dbModel => res.json(dbModel)) .catch(err => res.status(422).json(err)); } }
路由代码
const router = require("express").Router(); const calendarController = require("../../controllers/calendarController"); const passport = require("passport"); router.route("/") .get(calendarController.findAll) .post(calendarController.create); router.route("/:user") .get(calendarController.findByUser); router.route("/:id") .get(calendarController.findById) .put(calendarController.update) .delete(calendarController.remove); module.exports = router;
遇到的问题
当前按上述路由顺序,findById返回空数组;如果交换/:user和/:id的路由顺序,findById可以正常工作,但findByUser返回null。想知道这是什么原因,以及如何实现分别通过ID和用户ID正确查询文档?
问题原因分析
这是Express路由匹配机制导致的典型问题:Express的路由是从上到下按顺序匹配的,只要找到第一个匹配的路由规则,就会停止匹配后续路由。
- 当
/:user路由在前时:任何带单个路径参数的请求(比如/60d21b4667d0d8992e610c85这样的MongoDB ID)都会被/:user捕获,Express会把这个ID当作user参数传给findByUser方法,而数据库中没有以这个ID为用户ID的事件,所以返回空数组。 - 当
/:id路由在前时:同理,任何单个参数的请求(比如/user123)都会被/:id捕获,Express把user123当作事件ID传给findById,自然查不到对应事件,返回null。
解决方案
方案1:给路由添加区分前缀(推荐)
最清晰可靠的方法是给两个路由加上不同的前缀,让Express能明确区分请求意图,避免参数冲突。修改后的路由代码如下:
const router = require("express").Router(); const calendarController = require("../../controllers/calendarController"); const passport = require("passport"); router.route("/") .get(calendarController.findAll) .post(calendarController.create); // 给用户路由添加/user前缀 router.route("/user/:user") .get(calendarController.findByUser); // 给事件ID路由添加/event前缀 router.route("/event/:id") .get(calendarController.findById) .put(calendarController.update) .delete(calendarController.remove); module.exports = router;
这样请求时就可以明确区分:
- 查询用户事件:
GET /api/calendar/user/xxx - 查询单个事件:
GET /api/calendar/event/xxx
方案2:添加参数格式校验(备选)
如果不想修改路由结构,可以在控制器方法中先校验参数格式,判断参数是否符合MongoDB ObjectId的规则,再决定是否处理或交给下一个路由。
首先需要引入mongoose(因为要校验ObjectId),然后修改findById方法:
const mongoose = require("mongoose"); // 引入mongoose const db = require("../models"); module.exports = { // ...其他方法不变 findById: function(req, res, next) { // 校验参数是否为有效的MongoDB ObjectId if (!mongoose.Types.ObjectId.isValid(req.params.id)) { return next(); // 不符合格式,交给下一个路由(findByUser)处理 } db.Event .findById(req.params.id) .then(dbModel => res.json(dbModel)) .catch(err => res.status(422).json(err)); }, // ...其他方法不变 }
同时要确保路由顺序是/:id在前,/:user在后。不过这种方法有局限性:如果你的用户ID恰好符合MongoDB ObjectId的格式,还是会出现匹配错误,所以更推荐方案1。
内容的提问来源于stack exchange,提问作者Ashley Edds
相关产品推荐
相关产品推荐

