Nuxt路由守卫设计过程中很容易忽略的问题 如何避免未知路由被错误重定向到登录页

在使用Nuxt.js开发应用时,路由守卫(Route Guards)是保护页面访问权限的重要工具。然而,很多开发者在实现路由守卫时会遇到一个常见但容易被忽视的问题:当用户访问一个不存在的路由时,也会被强制重定向到登录页面。这不仅影响了用户体验,还可能导致一些意外的行为。本文将详细介绍这个问题的成因和解决方案。

假设我们有一个Nuxt应用,配置了如下的路由守卫中间件:·

// middleware/auth.client.ts
export default defineNuxtRouteMiddleware(async (to) => {
  const auth = useAuthStore()
  const loggedIn = auth.loggedIn || !!useCookie<string | null>('access_token').value

  const publicPaths = ['/login', '/register', '/forgot-password', '/']
  const isPublic = publicPaths.includes(to.path)
  
  // 未登录用户访问受保护页面时跳转到登录
  if (!isPublic && !loggedIn) {
    return navigateTo('/login')
  }
})

这个守卫的逻辑看起来很合理:如果用户访问的不是公共路径且未登录,就重定向到登录页。

但是,当用户访问一个不存在的页面(比如 /test/ 或 /unknown-page)时,也会被重定向到登录页,而不是显示404页面。

问题根源
问题的根本原因在于:路由守卫无法区分”受保护的路由”和”不存在的路由”。

在我们的中间件逻辑中:

publicPaths 包含了已知的公共路径

所有不在 publicPaths 中的路径都被视为”需要登录的受保护路径”

但实际上,还有第三种情况:应用中根本不存在的路径。对于这些路径,我们不应该进行任何登录检查,而应该让Nuxt显示404页面。

解决方案
方法一:检查路由匹配状态(推荐)

export default defineNuxtRouteMiddleware(async (to) => {
  const auth = useAuthStore()
  const loggedIn = auth.loggedIn || !!useCookie<string | null>('access_token').value

  const publicPaths = ['/login', '/register', '/forgot-password', '/']
  const isPublic = publicPaths.includes(to.path)
  
  // 关键修复:检查当前路由是否匹配到了有效路由
  if (!to.matched || to.matched.length === 0) {
    // 如果是未知路由,直接返回,让Nuxt显示404页面
    return
  }
  
  if (!isPublic && !loggedIn) {
    return navigateTo('/login')
  }
})

核心原理:

to.matched 是Vue Router提供的属性,包含与当前路径匹配的所有路由记录

如果路径在Nuxt的路由系统中没有定义(pages目录下没有对应文件),to.matched会是空数组

通过这个判断,我们可以识别并放行未知路由
方法二:显式定义所有有效路由
如果你希望更精确地控制,可以显式列出所有有效路由:

const validRoutes = [
  '/', '/login', '/register', '/forgot-password',
  '/dashboard', '/profile', '/settings'
  // ... 所有你应用中实际存在的路由
]

export default defineNuxtRouteMiddleware(async (to) => {
  // ... 其他代码
  
  // 如果访问的路由不在有效路由列表中,直接返回
  if (!validRoutes.includes(to.path)) {
    return
  }
  
  // ... 权限检查逻辑
})

完整的最佳实践示例

// middleware/auth.client.ts
import { useAuthStore } from '../store/auth'

export default defineNuxtRouteMiddleware(async (to) => {
  const auth = useAuthStore()
  const loggedIn = auth.loggedIn || !!useCookie<string | null>('access_token').value

  const publicPaths = ['/login', '/register', '/forgot-password', '/', '/admin/login']
  const adminPaths = ['/admin/dashboard', '/admin/users']
  
  const isPublic = publicPaths.includes(to.path) || to.path.startsWith('/p/')
  const isAdminPath = to.path.startsWith('/admin/') && !publicPaths.includes(to.path)

  // 第一步:检查是否为未知路由
  if (!to.matched || to.matched.length === 0) {
    return // 让Nuxt显示404页面
  }

  // 第二步:登录状态检查
  if (!isPublic && !loggedIn) {
    if (isAdminPath) {
      return navigateTo('/admin/login')
    }
    return navigateTo('/login')
  }

  // 第三步:权限验证(仅客户端)
  if (isAdminPath && loggedIn && import.meta.client) {
    await nextTick()
    if (!auth.isAdmin) {
      return navigateTo('/admin/login')
    }
  }
})

在实现Nuxt路由守卫时,记住要考虑三种类型的路由:

公共路由 – 任何人都可以访问

受保护路由 – 需要登录才能访问

未知路由 – 应用中不存在的路径,应该显示404页面

通过检查 to.matched 数组,我们可以准确识别未知路由,避免不必要的登录重定向,提供更好的用户体验。

这个看似简单的问题提醒我们:在编写路由守卫时,要考虑所有可能的路由情况,而不仅仅是”已登录”和”未登录”两种状态。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注