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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 00:01:16