MEAN栈Node.js博客:如何通过Slug而非ID实现资源路由?
解决MEAN栈中通过Slug获取资源的路由问题
问题背景
开发基于MEAN栈(使用Express Router)的Node.js博客,MongoDB中已存储字符串类型的slug字段,希望将原本通过:id调用的路由改为通过:slug调用,但修改后要么出现类型转换错误(无法找到ID),要么页面加载后无数据显示。
原可正常工作的ID路由:
router.get("/resources/:id", async (req, res) => { let foundResource = await Resources.findById(req.params.id); var metaTitle = foundResource.title; var metaDescription = foundResource.description; var metaKeywords = foundResource.keywords; var metaPath = foundResource.image; if (foundResource) { let allResources = await Resources.find({}); res.render("insider/show", { Resources: allResources, resource: foundResource, metaTitle: metaTitle, metaDescription: metaDescription, metaKeywords:metaKeywords, metaPath:metaPath, page_name:'insider'}); } });
尝试修改的Slug路由(存在问题):
router.get("/resources/:slug", async (req, res) => { let foundResource = await Resources.find(req.params.slug); var metaTitle = foundResource.title; var metaDescription = foundResource.description; var metaKeywords = foundResource.keywords; var metaPath = foundResource.image; if (foundResource) { let allResources = await Resources.find({}); res.render("insider/show", { Resources: allResources, resource: foundResource, metaTitle: metaTitle, metaDescription: metaDescription, metaKeywords:metaKeywords, metaPath:metaPath, page_name:'insider'}); } });
模型代码:
// schema setup var mongoose = require("mongoose"); var resourceSchema = new mongoose.Schema({ title: String, image: String, url: String, resourceDetails: String, price: String, category: String, value: String, excerpt: String, slug: String, create: {type: Date, default: Date.now}, author: { id: { type: mongoose.Schema.Types.ObjectId, ref: "User" }, username: String }, }); module.exports = mongoose.model("Resources", resourceSchema);
错误原因分析
- 查询方法使用错误:
Resources.find(req.params.slug)写法错误,find方法需要传入查询条件对象(而非直接传字符串),且find返回匹配文档的数组,不是单个文档,应该用findOne获取单个匹配资源。 - 属性访问时机错误:如果
foundResource为null(未找到资源),直接访问foundResource.title会抛出错误,必须先判断资源存在再赋值元数据。 - 缺少异常捕获:
async函数未捕获Promise异常,一旦查询出错会导致服务器崩溃。
修正后的代码
1. 优化路由代码
router.get("/resources/:slug", async (req, res) => { try { // 通过slug查询单个资源,传入正确的查询条件对象 let foundResource = await Resources.findOne({ slug: req.params.slug }); if (!foundResource) { // 未找到资源时返回404提示 return res.status(404).send("资源不存在"); } // 找到资源后再赋值元数据 var metaTitle = foundResource.title; var metaDescription = foundResource.description; var metaKeywords = foundResource.keywords; var metaPath = foundResource.image; let allResources = await Resources.find({}); res.render("insider/show", { Resources: allResources, resource: foundResource, metaTitle, metaDescription, metaKeywords, metaPath, page_name:'insider' }); } catch (err) { // 捕获并输出错误,返回服务器错误响应 console.error("查询资源出错:", err); res.status(500).send("服务器内部错误"); } });
2. 优化模型(可选但推荐)
为slug字段添加唯一索引,确保每个资源的slug不重复,同时提升查询效率:
var resourceSchema = new mongoose.Schema({ // 其他字段保持不变 slug: { type: String, required: true, unique: true // 添加唯一约束 }, // 其他字段保持不变 });
关键注意事项
- Slug唯一性:必须保证每个资源的slug唯一,否则
findOne只会返回第一个匹配的资源,导致逻辑错误。 - 错误处理:始终在
async路由中添加try/catch块,捕获数据库查询或渲染过程中的异常。 - 404处理:当未找到对应slug的资源时,返回明确的404响应,避免前端出现空白页面。
内容的提问来源于stack exchange,提问作者Andrew Buccellato
相关产品推荐
相关产品推荐

