React+Node博客应用:创建文章时分类无法存入MongoDB求助
问题:创建文章时分类标签无法存入MongoDB的categories集合
环境:React ^18.2.0、Node.js v14.18.1,开发博客应用时,用户创建新文章的分类标签并未存入MongoDB的categories集合中。
前端Write组件代码
import { useContext, useState } from "react"; import "./write.css"; import { Context } from "../../context/Context"; import NoImage from "../../images/noImage.jpeg" import { axiosInstance } from "../../config"; import ReactQuill from 'react-quill'; import 'react-quill/dist/quill.snow.css'; export default function Write() { const [title, setTitle] = useState(""); const [desc, setDesc] = useState(""); const [cats, setCats] = useState([]); const [file, setFile] = useState(null); const { user } = useContext(Context); console.log(desc); const handleSubmit = async (e) => { e.preventDefault(); const newPost = { username: user.username, title, desc, categories: cats, }; if (file) { const data = new FormData(); const filename = Date.now() + file.name; data.append("name", filename); data.append("file", file); newPost.photo = filename; try { await axiosInstance.post("/upload", data); } catch (err) {} } try { // First, create the categories and post together const res = await axiosInstance.post("/posts", newPost); window.location.replace("/post/" + res.data._id); } catch (err) { console.log(err); } }; const putCatHandler = (e) => { if(e.keyCode !== 32) return //avoid space key at the first letter const value = e.target.value.trimStart(); //add all element if(!value.trim()) return setCats([...cats, value]); e.target.value = ""; } const deleteCat = (index) => { setCats(cats.filter((cat, i) => i !== index)); } return ( <div className="writeWrapper"> <div className="write"> {/* box1 */} { !file ? <img className="writeNoImg" src={NoImage} alt="" /> : <img className="writeImg" src={URL.createObjectURL(file)} alt="" /> } {/* box2 */} <form className="writeForm" onSubmit={handleSubmit}> <div className="writeFormGroup"> <input type="file" id="fileInput" onChange={(e) => setFile(e.target.files[0])} required={true} /> <input type="text" placeholder="Title..." className="writeInput" autoFocus={true} onChange={e=>setTitle(e.target.value)} required={true} autoCapitalize={true} /> </div> <div className="writeFormGroup"> { cats.map((cat, index) => ( <div className="catInputItem" key={index}> <span className="text">{cat}</span> <span className="close" onClick={() => deleteCat(index)}>×</span> </div> )) } <input type="text" onKeyDown={putCatHandler} autoCapitalize={true} disabled={cats.length >= 5 ? true : false} required={true} className="catsUserInput" style={cats.length >= 5 ? {marginBottom:'15px'} : {}} placeholder="Press space key after typing category..."/> { cats.length >= 5 ? (<span style={{ color: "red"}}>You can put only 5 categories</span>) : <></> } </div> <div className="writeFormGroup"> <ReactQuill theme="snow" className="editor" value={desc} onChange={setDesc} /> </div> <div className="writeFormGroup"> <button className="writeSubmit" type="submit"> Publish </button> </div> </form> </div> </div> ); }
后端posts路由代码
const router = require("express").Router(); const User = require("../models/User.js"); const Post = require("../models/Post.js"); const Category = require("../models/Category.js"); const bcrypt = require("bcrypt"); //Create post router.post("/", async (req, res) => { const { categories, ...postData } = req.body; try { // 1. Find or create categories const categoryPromises = categories.map(async (catName) => { const existingCategory = await Category.findOne({ name: catName }); if (existingCategory) { return existingCategory; } else { const newCategory = new Category({ name: catName }); return await newCategory.save(); } }); const resolvedCategories = await Promise.all(categoryPromises); // 2. Create a new post with the resolved categories const newPost = new Post({ ...postData, categories: resolvedCategories.map((cat) => cat._id), }); const savedPost = await newPost.save(); res.status(200).json(savedPost); } catch (err) { res.status(500).json(err); } }); //update post router.put("/:id", async (req,res) => { try { const post = await Post.findById(req.params.id); if(post.username === req.body.username){ try { const updatedPost = await Post.findByIdAndUpdate( req.params.id, { $set: req.body, }, { new: true } ); res.status(200).json(updatedPost); } catch (error) { res.status(500).json(error); } }else { res.status(401).json("You can update only your post."); } } catch (error) { res.status(500).json(error); } }); //Delete post router.delete("/:id", async (req,res) => { try { const post = await Post.findById(req.params.id); if(post.username === req.body.username){ try{ await post.delete(); res.status(200).json("Post has been deleted..."); } catch (error) { res.status(500).json(error); } }else { res.status(401).json("You can delete only your post."); } } catch (error) { res.status(500).json(error); } }); //GET post router.get("/:id", async (req, res) => { try { const post = await Post.findById(req.params.id); res.status(200).json(post); } catch (error) { res.status(500).json(error); } }) //Get all posts router.get("/", async (req, res) => { const username = req.query.user; const catName = req.query.cat; try { let posts; if(username){ posts = await Post.find({ username }); } else if(catName){ posts = await Post.find({ categories:{ $in: [catName], }, }); } else { posts = await Post.find(); } res.status(200).json(posts); } catch (error) { res.status(500).json(error); } }) module.exports = router;
Category模型代码
const mongoose = require("mongoose"); const CategorySchema = new mongoose.Schema( { name: { type: String, required: true, }, }, { timestamps: true } ); module.exports = mongoose.model("Category", CategorySchema);
排查与解决思路
1. 验证数据传输链路
- 前端在
handleSubmit中添加console.log(newPost.categories),确认提交的分类数组是否为非空字符串数组,有没有空值或格式错误。 - 后端在创建post的接口开头添加
console.log(req.body.categories),检查是否正确接收到前端传递的分类数据。如果没收到,确认express是否配置了json解析中间件:在app.js中添加app.use(express.json())。
2. 修复分类创建逻辑
- 添加唯一约束:给Category模型的
name字段添加unique: true,避免重复创建同名分类,同时能快速定位重复创建的错误:const CategorySchema = new mongoose.Schema( { name: { type: String, required: true, unique: true }, }, { timestamps: true } ); - 处理大小写匹配:前端输入的分类名可能存在大小写差异(如"JavaScript"和"javascript"),导致重复创建。可以修改查询逻辑忽略大小写:
const existingCategory = await Category.findOne({ name: { $regex: new RegExp(`^${catName}$`, 'i') } }); - 错误捕获与处理:给分类创建的Promise添加错误捕获,避免单个分类创建失败导致整个请求失败:
const categoryPromises = categories.map(async (catName) => { try { const existingCategory = await Category.findOne({ name: catName }); if (existingCategory) { return existingCategory; } else { const newCategory = new Category({ name: catName }); return await newCategory.save(); } } catch (err) { // 处理重复键错误,返回已存在的分类 if (err.code === 11000) { return await Category.findOne({ name: catName }); } throw err; } });
3. 检查数据库与模型配置
- 确认Category模型的文件路径是否正确,mongoose是否成功连接到MongoDB。
- 查看MongoDB日志,检查是否有写入权限不足、集合创建失败等问题。
4. 前端输入逻辑优化
- 检查
putCatHandler函数,确认用户输入的分类是否被正确过滤:比如用户输入多个空格后按空格,是否会被if(!value.trim()) return正确拦截。
内容的提问来源于stack exchange,提问作者naoto
相关产品推荐
相关产品推荐

