You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);

问题根源分析

  1. async/await与mongoose回调混用:同时使用mongoose回调模式和async/await,导致Koa无法正确等待异步操作完成,路由提前结束返回404。mongoose原生支持Promise,应直接用await调用,无需回调。
  2. 中间件顺序错误:koa-static放在路由之后,静态资源请求可能被路由拦截,且路由处理完后才处理静态资源,逻辑顺序混乱。
  3. 未等待数据库连接完成:服务器启动时直接监听端口,未等待MongoDB连接就绪,初始请求可能因数据库未连接导致查询失败。
  4. 冗余的.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 15:10:27