本文详解如何将 Nuxt 2 中依赖 app 对象(如 app.$axios、app.$cookies、app.store)的认证中间件,安全、规范地迁移至 Nuxt 3,重点使用 useNuxtApp() 获取上下文,并适配 Composition API 风格。
本文详解如何将 nuxt 2 中依赖 `app` 对象(如 `app.$axios`、`app.$cookies`、`app.store`)的认证中间件,安全、规范地迁移至 nuxt 3,重点使用 `usenuxtapp()` 获取上下文,并适配 composition api 风格。
在 Nuxt 3 中,全局 app 实例已被移除,取而代之的是基于 Composable 的模块化设计。原 Nuxt 2 中通过 middleware/auth.js 接收 app 参数的方式不再适用;Nuxt 3 的中间件必须使用 defineNuxtRouteMiddleware 显式声明,并通过 useNuxtApp() 主动获取所需能力(如 $axios、$cookies、pinia store 等)。以下是完整迁移方案:
✅ 步骤一:重写中间件为 defineNuxtRouteMiddleware
将 middleware/auth.js 重命名为 middleware/auth.ts(推荐 TypeScript),并按 Nuxt 3 规范重构:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
// middleware/auth.ts
export default defineNuxtRouteMiddleware(async (to, from) => {
const nuxt = useNuxtApp()
const { $axios, $cookies } = nuxt
// ✅ 替代 app.$axios 和 app.$cookies
// ✅ 注意:Nuxt 3 默认不内置 Vuex,推荐使用 Pinia —— 假设你已配置 authStore
const authStore = useAuthStore() // 来自 ~/stores/auth.ts
try {
console.log('Loading authentication...')
const csrfToken = await fetchCsrfToken($axios, $cookies)
const authResult = await checkAuth($axios, $cookies, 'accessToken', csrfToken)
authStore.setAuthResult(authResult.res)
if (authResult.res) {
const userInfo = await getUserInfo($axios, $cookies, csrfToken)
authStore.setUserInfo(userInfo.res)
console.log('User info loaded:', userInfo.res)
}
} catch (error) {
console.error('Authentication middleware error:', error)
}
})
// ✅ 提取为独立函数(保持可测试性)
async function fetchCsrfToken($axios: any, $cookies: any) {
try {
const response = await $axios.$get('/getcsrf')
const tokenInfo = response.token
console.log('CSRF token received:', tokenInfo)
return tokenInfo
} catch (error) {
console.error('Failed to fetch CSRF token:', error)
throw error
}
}
// ✅ 统一请求封装(推荐迁移到 ~/composables/useApi.ts)
async function ajaxRequest(
$axios: any,
url: string,
method: string,
data?: { token?: string; csrfToken?: string }
) {
const headers: Record<string string> = {
'Content-Type': 'application/json',
}
if (data?.token) headers.Authorization = `Bearer ${data.token}`
if (data?.csrfToken) headers['X-XSRF-TOKEN'] = data.csrfToken
try {
const res = await $axios({
method,
url,
headers,
data: data || undefined,
})
if (res.status === 200) return res.data
// 统一错误映射(可根据后端协议扩展)
const statusText = {
400: 'Bad Request',
401: 'Unauthorized',
403: 'Forbidden',
}[res.status] || `Request failed with status ${res.status}`
throw new Error(`${statusText}: ${JSON.stringify(res.data)}`)
} catch (error: any) {
throw new Error(`API request error: ${error.message || error}`)
}
}
async function checkAuth(
$axios: any,
$cookies: any,
tokenType: 'accessToken' | 'refreshToken',
csrfToken: string
) {
const token = tokenType === 'accessToken'
? $cookies.get('accessToken')
: $cookies.get('refreshToken')
if (!token) {
console.warn(`Missing ${tokenType} cookie`)
return { res: false }
}
try {
console.log(`${tokenType} verification in progress...`)
const res = await ajaxRequest($axios, '/check-auth', 'POST', {
token,
csrfToken,
})
if (tokenType === 'accessToken' && res === 'Refresh token required') {
await checkAuth($axios, $cookies, 'refreshToken', csrfToken)
return { res: false }
}
return { res }
} catch (error) {
console.error(`Auth check failed for ${tokenType}:`, error)
return { res: false }
}
}
async function getUserInfo($axios: any, $cookies: any, csrfToken: string) {
const token = $cookies.get('accessToken')
if (!token) throw new Error('Access token missing for user info fetch')
try {
const res = await ajaxRequest($axios, '/user-info', 'POST', {
token,
csrfToken,
})
return { res }
} catch (error) {
console.error('Failed to fetch user info:', error)
throw error
}
}</string>
✅ 步骤二:确保依赖项已正确安装与配置
- Axios:使用 @nuxtjs/axios 已被弃用;推荐直接使用 Nuxt 内置的 $fetch 或集成 axios 实例(通过 runtimeConfig + create axios instance + provide 注入)。
-
Cookies:$cookies 来自 @vueuse/core 或 nuxt-cookie-control;若需服务端兼容,请使用 useCookie()(Nuxt 3 原生支持):
const accessToken = useCookie('accessToken', { httpOnly: false, secure: true }) -
Store:务必迁移到 Pinia(Nuxt 3 官方推荐状态管理),并在 ~/stores/auth.ts 中定义:
export const useAuthStore = defineStore('auth', { state: () => ({ authResult: null as any, userInfo: null as any, }), actions: { setAuthResult(data) { this.authResult = data }, setUserInfo(data) { this.userInfo = data }, } })
⚠️ 注意事项与最佳实践
- ❌ 不要直接在中间件中调用 useRouter() 或 navigateTo() 后不 return —— 若需重定向,请显式返回:
if (!authStore.isAuthenticated) { return navigateTo('/login', { redirectCode: 302 }) } - ✅ 所有异步操作必须 await,中间件默认是 async,未 await 将导致竞态或静默失败。
- ✅ 避免在中间件中执行耗时逻辑(如多次 API 调用)—— 可考虑预加载策略(useAsyncData + #lazy)或服务端校验兜底。
- ✅ 开发阶段启用 debug: true(在 nuxt.config.ts 中)便于排查 useNuxtApp() 作用域问题。
通过以上重构,你不仅完成了从 app 到 useNuxtApp() 的平滑过渡,更拥抱了 Nuxt 3 的响应式、类型安全与模块化哲学。迁移不是简单替换,而是借机优化架构 —— 让认证逻辑更清晰、可维护、可测试。










