Nuxt.js中间件报错:Cannot set headers after they are sent to the client
问题分析与修复方案
错误根源
Cannot set headers after they are sent to the client 错误的核心原因是:在服务器端,HTTP响应已经发送给客户端之后,代码仍尝试修改响应头。这里具体是$auth.logout()会尝试设置Cookie(修改响应头),但此时请求已经处理完成,响应早已发出。
你的代码存在的问题
服务器端使用
setInterval的风险
Nuxt中间件在服务器端运行时,会绑定到当前请求上下文。你在中间件中使用setInterval,这个定时器会脱离当前请求上下文继续运行。当原请求的响应已经发送给客户端后,定时器里的refreshToken执行失败并调用$auth.logout(),此时再尝试设置Cookie就会触发报错。未区分客户端/服务器端执行逻辑
令牌刷新逻辑应该只在客户端运行,因为服务器端的请求是一次性的,不存在持续运行的上下文来维护定时器。你的中间件没有做环境判断,导致服务器端也会执行定时器逻辑。错误处理中的响应头操作时机问题
当refreshToken在服务器端的定时器中执行失败时,调用$auth.logout()会尝试修改已发送的响应头,直接触发错误。
修复步骤
1. 限定逻辑仅在客户端执行
在中间件开头添加环境判断,确保令牌刷新逻辑只在客户端运行:
export default async function ({ app, process }) { // 仅在客户端执行逻辑 if (process.server) return const { $api, $auth } = app // ... 剩余代码 }
2. 移除服务器端的定时器逻辑
因为服务器端无法维护跨请求的定时器,所有定时刷新逻辑都放在客户端执行,避免脱离请求上下文的操作。
修复后的完整代码
function decodeToken (token) { const base64 = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString('utf-8')) const exp = base64.exp const now = Date.now() / 1000 let timeUntilRefresh = exp ? exp - now : false timeUntilRefresh -= (1 * 60) return timeUntilRefresh } async function refreshToken ($api, $auth, refresh_token) { try { if (refresh_token) { const response = await $api.post('api/private/user/refresh-token', { refresh_token: refresh_token }) $auth.setUserToken(response.data.token, response.data.refresh_token) return decodeToken($auth.strategy.refreshToken.get()) } } catch (err) { $auth.logout() throw new Error('Error while refreshing token') } } export default async function ({ app, process }) { // 仅在客户端执行令牌刷新逻辑 if (process.server) return const { $api, $auth } = app let refresh_token = $auth.strategy.refreshToken.get() let refreshInterval = 500 if (refresh_token) { const timeUntilRefresh = decodeToken(refresh_token) refreshInterval = timeUntilRefresh if (timeUntilRefresh <= 300 && timeUntilRefresh >= 0) { await refreshToken($api, $auth, refresh_token) refreshInterval = 500 } if (refreshInterval < 0) { refreshInterval = await refreshToken($api, $auth, refresh_token) } setInterval(async function () { refresh_token = $auth.strategy.refreshToken.get() await refreshToken($api, $auth, refresh_token) }, refreshInterval * 1000) } }
额外提示
- Nuxt的
process.server和process.client可以用来区分代码运行环境,确保敏感逻辑在正确的环境执行。 - 令牌刷新逻辑放在客户端更合理,因为客户端需要持续维护会话状态,而服务器端是无状态的请求处理模式。
内容的提问来源于stack exchange,提问作者Zeeps Wear
相关产品推荐
相关产品推荐

