Koa博客应用路由随机加载失败,频繁出现Not Found问题求助
Koa博客随机404/加载失败问题排查与修复
我用Koa搭配koa-nunjucks-2做模板引擎、MongoDB做数据库搭了个简易博客。本地测试时所有路由都会随机加载成功或显示“Not Found”,得多次刷新重试才可能正常显示。没加async/await时页面只能加载一次,刷新就失效,现在对async/await理解基础,找不到问题根源,相关index.js代码如下:
////////////////////////// //Initializing Environment Variables //npm i dotenv //npm i koa-methodoverride //npm i koa-bodyparser ////////////////////////// require('dotenv').config(); const override = require('koa-methodoverride'); const parser = require('koa-bodyparser'); ////////////////////////// //Connecting the DB //npm i mongoose ////////////////////////// const mongoose = require('mongoose'); const db = mongoose.connection; const host = process.env.host; const dbupdate = { useNewUrlParser: true, useUnifiedTopology: true}; mongoose.connect(host, dbupdate); mongoose.connection.on db.on('error', (err) => console.log('Error, DB Not connected')); db.on('connected', () => console.log ('connected to mongo')); db.on('disconnected', () => console.log ('Mongo is disconnected')); db.on('open', () => console.log ('Connection Made!')); ////////////////////////// //Model Schema ////////////////////////// const Recipe = require('./model/recipe.js'); ////////////////////////// //Create Our Server Object ////////////////////////// const koa = require('koa'); const server = new koa(); ////////////////////////// //Create Our Static Folder //npm i koa-static ////////////////////////// const static = require('koa-static'); ////////////////////////// //Creating Our Router //npm i koa-router ////////////////////////// const Router = require('koa-router'); const route = Router(); ////////////////////////// //Initializing views //npm i koa-views //npm i nunjucks ////////////////////////// // const views = require('koa-views'); // const nunj = require('nunjucks'); // nunj.configure('./views', {autoescape: true}); const koaNunjucks = require('koa-nunjucks-2'); const path = require('path'); ////////////////////////// //Routes //Route.get - route.post - route.patch - post.put - route.delete ////////////////////////// route.get('/', async (ctx, next) => { console.log('connected to root route'); return Recipe.find({}, async (error, results) => { console.log(results); await ctx.render('index', { posts: results, name: process.env.NAME }); }).clone() }); route.get('/view/:id', async (ctx, next) => { console.log('connected to recipe route'); return Recipe.findById(ctx.params.id, async (error, results) => { console.log(results) await ctx.render('recipe', { post: results }); }).clone() }); route.post('/admin', async (ctx, next) => { return Recipe.find({}, async (error, results) => { console.log(ctx.request.body) console.log(process.env.pw) if(ctx.request.body.pw === process.env.pw) { console.log('Connected to Admin Route'); return await ctx.render('admin', { posts: results }); } else { console.log('Connected to Index Route'); await ctx.redirect('/'); } }).clone() }); // route.get('/', (ctx, next) => { // return ctx.render('index.html', { // name: process.env.NAME // }) // }); // route.get('/:name', (ctx, next) => { // return ctx.render('./index.html', { // name: ctx.params.name // }) // }); ////////////////////////// //Middleware ////////////////////////// // server.use(views('./views', {map: {html: 'nunjucks'}})); server.use(override('_method')); server.use(parser()); server.use(koaNunjucks({ ext: 'njk', path: path.join(__dirname, 'views'), nunjucksConfig: { trimBlocks: true } })); server.use(route.routes()); server.use(static('./public')); ////////////////////////// //Our Listener on Port 1985 ////////////////////////// server.listen(1985);
问题根源分析
- async/await与mongoose回调混用:同时使用mongoose回调模式和async/await,导致Koa无法正确等待异步操作完成,路由提前结束返回404。mongoose原生支持Promise,应直接用await调用,无需回调。
- 中间件顺序错误:
koa-static放在路由之后,静态资源请求可能被路由拦截,且路由处理完后才处理静态资源,逻辑顺序混乱。 - 未等待数据库连接完成:服务器启动时直接监听端口,未等待MongoDB连接就绪,初始请求可能因数据库未连接导致查询失败。
- 冗余的
.clone()调用:回调模式下的.clone()是多余操作,会导致查询重复执行,加剧异步流程混乱。
修复后的代码
////////////////////////// //Initializing Environment Variables //npm i dotenv //npm i koa-methodoverride //npm i koa-bodyparser ////////////////////////// require('dotenv').config(); const override = require('koa-methodoverride'); const parser = require('koa-bodyparser'); ////////////////////////// //Connecting the DB //npm i mongoose ////////////////////////// const mongoose = require('mongoose'); const db = mongoose.connection; const host = process.env.host; const dbupdate = { useNewUrlParser: true, useUnifiedTopology: true }; // 封装数据库连接为异步函数,确保连接成功后再启动服务 async function connectDB() { try { await mongoose.connect(host, dbupdate); console.log('Connected to mongo'); } catch (err) { console.log('Error, DB Not connected', err); process.exit(1); // 连接失败直接退出进程 } } db.on('disconnected', () => console.log('Mongo is disconnected')); db.on('open', () => console.log('Connection Made!')); ////////////////////////// //Model Schema ////////////////////////// const Recipe = require('./model/recipe.js'); ////////////////////////// //Create Our Server Object ////////////////////////// const koa = require('koa'); const server = new koa(); ////////////////////////// //Create Our Static Folder //npm i koa-static ////////////////////////// const static = require('koa-static'); ////////////////////////// //Creating Our Router //npm i koa-router ////////////////////////// const Router = require('koa-router'); const route = Router(); ////////////////////////// //Initializing views //npm i koa-views //npm i nunjucks ////////////////////////// const koaNunjucks = require('koa-nunjucks-2'); const path = require('path'); ////////////////////////// //Routes //Route.get - route.post - route.patch - post.put - route.delete ////////////////////////// route.get('/', async (ctx) => { console.log('connected to root route'); // 使用await直接等待mongoose查询结果,无需回调 const results = await Recipe.find({}); console.log(results); await ctx.render('index', { posts: results, name: process.env.NAME }); }); route.get('/view/:id', async (ctx) => { console.log('connected to recipe route'); const results = await Recipe.findById(ctx.params.id); console.log(results); await ctx.render('recipe', { post: results }); }); route.post('/admin', async (ctx) => { console.log(ctx.request.body); console.log(process.env.pw); if (ctx.request.body.pw === process.env.pw) { console.log('Connected to Admin Route'); const results = await Recipe.find({}); await ctx.render('admin', { posts: results }); } else { console.log('Redirecting to Index Route'); ctx.redirect('/'); // redirect不需要await,Koa会自动处理 } }); ////////////////////////// //Middleware ////////////////////////// // 调整中间件顺序:静态资源优先处理 server.use(static('./public')); server.use(override('_method')); server.use(parser()); server.use(koaNunjucks({ ext: 'njk', path: path.join(__dirname, 'views'), nunjucksConfig: { trimBlocks: true } })); server.use(route.routes()); server.use(route.allowedMethods()); // 处理OPTIONS请求与状态码规范 ////////////////////////// //Our Listener on Port 1985 ////////////////////////// // 等待数据库连接成功后再启动服务 connectDB().then(() => { server.listen(1985, () => { console.log('Server running on port 1985'); }); });
关键修复点说明
- 统一async/await异步处理:利用mongoose的Promise特性,直接用await等待查询结果,彻底避免回调与async/await混用导致的流程混乱。
- 调整中间件顺序:
koa-static移至最前,优先处理静态资源请求,避免路由误拦截。 - 数据库连接就绪后启动服务:确保所有请求处理前数据库已连接,避免初始请求查询失败。
- 移除冗余操作:删除不必要的
.clone()调用,简化查询逻辑。 - 添加
route.allowedMethods():规范HTTP状态码返回,处理OPTIONS请求,避免无匹配路由时返回错误的404。
内容的提问来源于stack exchange,提问作者Nicholas Kelly
相关产品推荐
相关产品推荐

