Node.js Express中间件重定向失效问题排查求助
问题描述
我尝试用中间件在英文根路由/和语言路由/:lang之间实现重定向:检测到浏览器语言非英语时,跳转到对应语言路径并返回翻译后的HTML文件。但用中间件重定向时,浏览器地址栏会显示/it这类路径,却没有触发任何路由方法,重定向没真正生效。把重定向逻辑直接移到/路由方法里时,功能就正常。请问我哪里出错了?
使用中间件的代码
app.use((req, res, next) => { var language = req.acceptsLanguages('fr', 'es', 'en', 'it', 'de'); console.log('browser language is: ', language); if (language !== 'en') { return res.redirect(`/${language}`); } next(); }); app.get('/:lang', (req, res) => { console.log('get /lang called'); // const lang = req.lang; // undefined const lang = req.acceptsLanguages('fr', 'es', 'en', 'it', 'de'); console.log('lang is: ', lang) res.sendFile(__dirname + `/index-${lang}.html`); }); app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html'); console.log('get / called'); });
不使用中间件的代码
app.get('/:lang', (req, res, next) => { const lang = req.params.lang; console.log('get /lang called'); console.log('lang is: ', lang); res.sendFile(__dirname + `/index-${lang}.html`); }); app.get('/', (req, res, next) => { const defaultLang = 'en'; // Default language is English const lang = req.acceptsLanguages('fr', 'es', 'en', 'it', 'de') || defaultLang; console.log('get / called'); console.log('browser language is: ', lang); if (lang !== defaultLang) { return res.redirect(`/${lang}`); } res.sendFile(__dirname + '/index.html'); });
问题原因及解决方法
核心问题
你用的是全局中间件(app.use不带路径参数),它会拦截所有请求,包括重定向后的/it、/fr这类语言路由请求。当浏览器跳转到/it后,中间件再次检测到语言不是en,又会发起重定向到/it,陷入无限重定向循环——浏览器一直在处理重定向请求,根本没机会进入/:lang的路由处理逻辑,所以你看不到路由里的日志输出。
解决办法
有两种可行的修正方式:
方式一:让中间件只作用于根路由/
把全局中间件改成仅对/路径生效,这样只有访问根路由时才会触发重定向逻辑,语言路由的请求不会被中间件拦截:
// 只对根路由应用中间件 app.use('/', (req, res, next) => { var language = req.acceptsLanguages('fr', 'es', 'en', 'it', 'de'); console.log('browser language is: ', language); if (language !== 'en') { return res.redirect(`/${language}`); } next(); }); // 后续路由代码不变,注意改用URL参数里的语言值 app.get('/:lang', (req, res) => { console.log('get /lang called'); const lang = req.params.lang; console.log('lang is: ', lang) res.sendFile(__dirname + `/index-${lang}.html`); }); app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html'); console.log('get / called'); });
方式二:在中间件里排除语言路由的请求
在全局中间件里判断当前请求路径是否已经是语言路由,如果是就直接放行,避免循环:
app.use((req, res, next) => { // 先判断当前路径是否是已有的语言路由 const langRegex = /^\/(fr|es|en|it|de)$/; if (langRegex.test(req.path)) { return next(); } var language = req.acceptsLanguages('fr', 'es', 'en', 'it', 'de'); console.log('browser language is: ', language); if (language !== 'en') { return res.redirect(`/${language}`); } next(); }); // 后续路由代码不变,注意改用URL参数里的语言值 app.get('/:lang', (req, res) => { console.log('get /lang called'); const lang = req.params.lang; console.log('lang is: ', lang) res.sendFile(__dirname + `/index-${lang}.html`); }); app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html'); console.log('get / called'); });
另外补充一点:你的/:lang路由里原本用req.acceptsLanguages获取语言是不合理的,应该直接用req.params.lang——因为用户已经通过URL指定了语言,应该优先使用路径里的语言参数,而不是再次检测浏览器语言。
内容的提问来源于stack exchange,提问作者Vincenzo
相关产品推荐
相关产品推荐

