Express Router被调用两次的原因排查及解决
问题:Express路由中间件被触发两次的原因及解决方法
问题描述
运行server.js后,通过路由调用index.js,其中router.use(async (req, res, next) => {})内的console.log(req.path)会输出两次"/",但仅手动发起了一次页面请求。浏览器网络面板显示localhost:3001/被请求两次,且index.js仅加载一次,实际是router.get('/')被触发两次。
相关代码
server.js
const express = require('express'); const categoryService = new CategoryService(path.join(__dirname, 'data', 'categories.json')); const routes = require('./routes/index')({ categoryService }); const app = express(); app.use((request, response, next) => { response.locals.homeVar = 'Home'; next(); }); app.use((request, response, next) => { cookieSession({ name: 'session', keys: ['ABC', 'DEF'], maxAge: 1 * 60 * 60 * 1000, // 24 hours })(request, response, next); }); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.locals.siteName = 'Together Mart'; app.use(routes); app.use((request, response, next) => { next(createError(404, 'File not found')); }); app.use((err, req, res) => { res.status(err.status || 500); res.locals.error = err.message; res.locals.errorStatus = err.status; res.locals.errorStack = err.stack || ''; if (err.status === 404) { res.locals.error = `The requested file '${req.originalUrl}' could not be found`; } if (err.name === 'CssSyntaxError') { res.locals.error += `\n\nError in file '${err.filename}', line ${err.line}, column ${err.column}:\n${err.reason}`; } if (err.view) { res.locals.error += `\n\nError rendering '${err.view}'`; } console.error(err); res.render('error'); });
index.js
const express = require('express'); const router = express.Router(); let categoryData; module.exports = (params) => { console.log('Inside index.js'); let categoryList = null; router.use(async (req, res, next) => { console.log(req.path); if (!categoryList) { try { const { categoryService } = params; categoryList = await categoryService.getNames(); categoryData = await categoryService.getListShort(); } catch (err) { next(err); } } res.locals.categoryList = categoryList; res.locals.categoryData = categoryData; next(); }); router.get('/', (req, res) => { res.render('layout/index', { pageTitle: 'Home Page', template: 'index' }); }); router.get('/category/:name/subcategories', async (req, res, next) => { try { const { categoryService } = params; const subcategories = await categoryService.getSubCategoriesForCategory( req.params.name.toString() ); console.log(req.params.name); console.log(subcategories); res.json(subcategories); } catch (err) { next(err); } }); return router; };
原因分析
1. 浏览器自动请求favicon.ico
绝大多数浏览器会自动向网站根路径请求/favicon.ico作为网站图标。如果服务器未配置专门处理该请求的路由,且路由匹配规则存在疏漏,这个请求可能会被错误导向到根路径的处理逻辑,导致看起来根路径被请求两次。
2. 页面模板资源路径错误
如果渲染的layout/index模板中,存在资源引用(如图片、脚本、样式)的src/href属性错误指向根路径/,浏览器会将其视为需要加载的资源,从而再次发起根路径请求。
解决方法
方法1:处理favicon.ico请求
在app.use(routes)之前添加专门的图标请求处理逻辑:
// 直接返回204无内容,避免触发后续路由 app.get('/favicon.ico', (req, res) => res.status(204).end());
或者使用官方中间件提供图标:
const favicon = require('serve-favicon'); const path = require('path'); app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
方法2:检查页面模板资源引用
打开layout/index模板文件,逐一检查所有src、href属性,确保没有错误指向根路径/。例如:
- 错误写法:
<img src="/"> - 正确写法:
<img src="/assets/images/logo.png">(根据实际资源路径调整)
方法3:验证请求差异
在路由中间件中打印完整请求信息,明确两次请求的区别:
router.use(async (req, res, next) => { console.log(`路径: ${req.path}, 方法: ${req.method}, 客户端标识: ${req.get('User-Agent')}`); // 原有逻辑 next(); });
通过User-Agent可区分是页面请求还是图标请求(图标请求的标识通常包含favicon关键词)。
内容的提问来源于stack exchange,提问作者Bharath
相关产品推荐
相关产品推荐

