在Nuxt 3中使用defineNuxtRouteMiddleware实现导航保护

**由 Paul Akinyemi✏️ 撰写**

导航保护功能可让您控制谁可以访问应用程序的特定部分,确保只有授权用户才能访问特定页面。这可增强安全性并改善用户体验,让您的应用程序安全高效。

在本文中,我们将介绍导航保护是什么、它们与中间件的关系以及如何在 Nuxt 3 中编写导航保护。术语“中间件”将用于指代路由中间件,它在 Nuxt 应用程序的 Vue 部分运行,与 Nuxt 的服务器中间件不同。

什么是导航守卫?

导航守卫是一些在页面请求之后但呈现之前运行的代码,用于确保只有授权用户才能访问某些功能。导航守卫用于实现以下功能:

  • 确保用户必须登录才能访问应用程序
  • 实施基于角色的访问控制
  • 实现功能标志
  • 使用 Nuxt 中间件创建导航守卫

    为了创建导航保护,我们将利用 Nuxt 中称为中间件的概念。Nuxt 中的中间件是在页面呈现之前运行的特殊函数,使其成为实现导航保护的理想选择。

    Nuxt 中有三种类型的中间件:匿名、命名和全局。当你想使用匿名和命名中间件时,你必须在要保护的页面上明确定义它。相反,全局中间件适用于所有页面。

    中间件语法

    这是所有中间件采用的一般形式:

    (from, to) => {
      if (someCondition) {
        return navigateTo("/route")
      }
    
      if (otherCondition) {
        return abortNavigation()
        //or
        return abortNavigation(error)
      }
    
      // return nothing to allow the request to proceed normally
    }

    请注意,中间件也可以是“异步”函数,如果需要,它允许您“等待”异步请求。

    每个中间件都是一个接受两个路由对象作为参数的函数:

  • to 对象表示用户想要导航到的位置
  • from 对象表示用户发出请求之前所在的位置
  • 以下是“route”对象的几个属性:

  • **fullPath**:表示整个位置的字符串,包括搜索和哈希。此字符串是百分比编码的(百分比编码是在浏览器中输入 URL 时应用的转换)
  • **params**:从路径中提取的 URL 参数对象
  • 您可以在此处找到完整的属性列表。

    函数“navigateTo”和“abortNavigation”允许您实现导航保护功能。“navigateTo”允许您将用户重定向到另一条路径,而“abortNavigation”允许您完全终止导航。这两个函数都是全局可用的,因此不需要导入。

    现在我们知道如何编写中间件函数,让我们看看如何为特定页面或全局注册一个中间件函数。

    编写匿名中间件

    匿名中间件,也称为内联中间件,是在页面内部定义的中间件,因此仅在该页面上运行。

    要编写匿名中间件,您只需在编译器宏的“中间件”数组内编写中间件函数。

    以下是一个例子:

    // pages/staff.vue
    
    

    注意:你可以在一个页面上定义多个匿名中间件,它们将按照它们在“中间件”数组中出现的顺序执行。

    就这样!接下来,我们将研究如何编写命名中间件。

    使用 defineNuxtRouteMiddleware 编写命名中间件

    命名中间件是在 `middleware` 目录中的独立文件中定义的中间件,可以在多个页面上运行。中间件的名称是其包含文件的短横线命名名称,因此位于文件 `middleware/isAdmin.js` 中的中间件将获得名称 `is-admin`。

    要创建命名中间件,我们还需要一件事:一个名为“defineNuxtRouteMiddleware”的辅助函数。此辅助函数将中间件函数作为其唯一参数。

    要创建一个命名中间件,首先必须在 `middleware` 目录中创建一个文件,然后编写中间件,将其传递给 `defineNuxtRouteMiddleware`,并导出返回值。以下是示例:

    // middleware/isAdmin.js
    export default defineNuxtRouteMiddleware((to, from) => {
          // isUserAdmin() is a dummy function
          const isAdmin = isUserAdmin()
    
          if (!isAdmin) {
            // Redirect the user to the homepage
            return navigateTo('/')
          }
    
          // Else, continue with navigation
        })

    现在您已经创建了 `is-admin` 中间件,您可以通过将其名称放入 `definePageMeta` 宏的 `middleware` 数组中来在任何页面上使用它。以下是一个例子:

    // pages/staff.vue
    
    

    就是这样!

    编写全局中间件

    编写全局中间件的方式与编写命名中间件的方式相同,但有一点不同:中间件文件的名称必须以 `.global` 为后缀。因此,该文件将被命名为 `middleware/isAdmin.global.js`,而不是 `middleware/isAdmin.js`,然后它将在每个路由上运行,而无需在 `definePageMeta` 中指定。

    请注意,无法为全局中间件定义异常 — 它们始终在每个路由上运行。如果您希望全局中间件跳过某个路由,则必须在中间件内部编写检查。

    中间件排序

    在任何给定的路由上,中间件按以下顺序执行:

  • 全局中间件
  • definePageMeta 的中间件数组中定义的中间件,按出现的顺序
  • 全局中间件根据文件名按字母顺序执行,但如果您需要按特定顺序运行中间件,则可以在其前面加上数字,例如:“middleware/01.isAdmin.global.js”。

    使用导航守卫时的最佳实践

    以下是编写导航保护时需要牢记的一些准则。

    导航卫士应该精简

    您应该编写导航保护来做尽可能少的工作;每个函数运行的时间越长,用户等待页面呈现的时间就越长。

    如果可能的话,您应该避免发出网络请求,如果不能,您应该一次获取和缓存尽可能多的数据。

    导航守卫应该是纯函数

    尽管 Nuxt 保证了中间件的执行顺序,但每个中间件函数都应该没有副作用,并且无论在中间件链中的哪个位置执行都应该完全相同地运行。

    小心无限重定向

    如果不小心,通过编写如下中间件很容易创建无限重定向:

    export default defineNuxtRouteMiddleware((to, from) => {
        const { isAuthenticated } = dummyAuthFunction();
    
        if (!isAuthenticated) {
            return navigateTo('/login')
        } else {
            return navigateTo('/')
        }
    })

    这个中间件乍一看没什么问题,但它有一个问题:无论条件值是什么,你总是在重定向。如果这个中间件在你访问要重定向到的页面时也会执行,这将创建一个无限循环。以下是修改代码以修复此问题的方法:

    export default defineNuxtRouteMiddleware((to, from) => {
        const { isAuthenticated } = dummyAuthFunction();
    
        if (!isAuthenticated) {
            return navigateTo('/login')
        }
    })

    这里的关键是,当您不需要转移用户时,不要重定向到特定路线,而是返回任何内容(允许导航继续)。

    结论

    导航保护是 Nuxt 3 中的一项强大功能,可帮助您控制对应用程序路由的访问。通过利用中间件(无论是匿名、命名还是全局),您可以高效地实施这些保护以增强安全性和用户体验。保持中间件精简和纯粹,您将确保整个应用程序的导航顺畅而有效。

    几分钟内即可设置 LogRocket 的现代错误跟踪:

  • 访问 https://logrocket.com/signup/ 获取应用程序 ID。
  • 通过 NPM 或脚本标签安装 LogRocket。LogRocket.init() 必须在客户端调用,而不是服务器端。
  • 新版本:

    $ npm i --save logrocket 
    
    // Code:
    
    import LogRocket from 'logrocket'; 
    LogRocket.init('app/id');

    脚本标记:

    Add to your HTML:
    
    
    

    3.(可选)安装插件以便与您的堆栈进行更深入的集成:

  • Redux 中间件
  • ngrx 中间件
  • Vuex 插件
  • 现在就开始。