页面中间件

在页面进入前执行校验、重定向与导航控制

页面中间件用于在进入页面前执行登录校验、权限校验、参数检查、页面重定向等逻辑。它和页面文件、definePageMeta 配合使用:中间件负责导航判断,页面负责声明需要哪些中间件。

中间件是页面导航的守卫,不是请求拦截器。需要处理接口请求前后的统一逻辑时,请使用 http 提供的生命周期钩子。

定义中间件

中间件文件放在项目的 src/middlewares 目录下,并通过默认导出 defineRouteMiddleware

目录结构
src
└─ middlewares
   ├─ auth.global.ts
   └─ permission.ts

中间件接收目标路由 to 和来源路由 from

export default defineRouteMiddleware((to, from) => {
  console.log("目标页面:", to.path);
  console.log("当前页面:", from.path);
});

其中:

  • to 表示即将进入的目标页面;
  • from 表示当前页面,可用于根据来源页面决定是否放行。
defineRouteMiddleware API 参考:查看 to/from 包含的字段

中间件类型

全局中间件

文件名以 .global 结尾的中间件会应用到所有页面,不需要在页面中声明:

src/middlewares/auth.global.ts
src/middlewares/auth.global.ts
const WHITE_LIST = ["/pages/index/index", "/pages/login/login"];

export default defineRouteMiddleware((to, from) => {
  if (WHITE_LIST.includes(to.path)) {
    return;
  }

  if (!uni.getStorageSync("token")) {
    return goTo("/pages/login/login", {
      replace: true,
    });
  }
});
全局中间件始终先于页面声明的具名中间件执行。

具名中间件

创建 src/middlewares/permission.ts

src/middlewares/permission.ts
export default defineRouteMiddleware(async (to, from) => {
  const user = await getCurrentUser();

  if (!user) {
    return goTo("/pages/login/login", { replace: true });
  }

  if (!user.hasPermission && to.path.startsWith("/pages/admin/")) {
    return abortNavigation(new Error("没有访问权限"));
  }
});

页面模块中通过 definePageMeta 使用:

src/pages/dashboard/index.vue
<script setup lang="ts">
definePageMeta({
  middleware: "permission",
});
</script>

多个具名中间件可以使用数组,数组中的中间件会按照声明顺序执行:

<script setup lang="ts">
definePageMeta({
  middleware: ["permission"],
});
</script>
页面中声明的是具名中间件名称;中间件名称由文件路径决定,不包含文件扩展名:
src/middlewares/permission.ts -> permission
src/middlewares/member/requires.ts -> member-requires

工作方式

请求进入页面时,Oiyo 会按以下顺序执行:

  1. 先执行所有全局中间件;
  2. 再按页面 definePageMeta 中声明的顺序执行具名中间件;
  3. 中间件根据返回的行为,引导当前导航放行/重定向/中止;

导航行为

放行

不返回值或返回 true,继续执行后续中间件:

export default defineRouteMiddleware(() => {
  // 不返回值,放行导航
});

重定向

使用 goTo() 创建重定向结果,并通过 return 返回:

export default defineRouteMiddleware((to) => {
  if (to.path !== "/pages/login/login" && !uni.getStorageSync("token")) {
    return goTo("/pages/login/login");
  }
});
goTo() 必须通过 return 返回。仅调用 goTo() 不会触发重定向。

携带查询参数

return goTo({
  path: "/pages/search/result",
  query: {
    keyword: "uni-app",
    page: 1,
  },
});

最终目标地址为:

/pages/search/result?keyword=uni-app&page=1

替换当前页面

普通页面默认使用 navigateTo。设置 replace: true 后使用 redirectTo

return goTo("/pages/login/login", {
  replace: true,
});

TabBar 页面

重定向到 TabBar 页面时会自动使用 switchTab

return goTo("/pages/index/index");

显式指定导航方式:

return goTo("/pages/login/login", {
  type: "reLaunch",
});

type 支持:

  • navigateTo
  • redirectTo
  • reLaunch
  • switchTab

重定向目标会重新执行目标页面的中间件。

中止

返回 falseabortNavigation(),中止当前导航:

export default defineRouteMiddleware((to) => {
  if (to.path === "/pages/forbidden/index") {
    return abortNavigation();
  }
});

可以传入中止原因:

return abortNavigation(new Error("没有访问权限"));

中止原因会传递给导航调用的 failcomplete 回调。

常见场景

登录校验

src/middlewares/auth.global.ts
const WHITE_LIST = ["/pages/index/index", "/pages/login/login"];

export default defineRouteMiddleware((to) => {
  if (WHITE_LIST.includes(to.path)) {
    return;
  }

  if (!uni.getStorageSync("token")) {
    return goTo("/pages/login/login", {
      replace: true,
    });
  }
});

权限校验

src/middlewares/permission.ts
export default defineRouteMiddleware(async (to) => {
  const user = await getCurrentUser();

  if (!user) {
    return goTo("/pages/login/login", { replace: true });
  }

  if (!user.hasPermission && to.path.startsWith("/pages/admin/")) {
    return abortNavigation(new Error("没有访问权限"));
  }
});

页面中使用:

<script setup lang="ts">
definePageMeta({
  middleware: ["permission"],
});
</script>

异步中间件

中间件支持异步函数:

export default defineRouteMiddleware(async (to) => {
  const user = await getCurrentUser();

  if (!user && to.path !== "/pages/login/login") {
    return goTo("/pages/login/login");
  }
});

常见用途包括:

  • 获取当前用户信息;
  • 查询用户权限;
  • 校验页面访问条件;
  • 获取页面进入前所需的配置。

definePageMeta API 参考:查看 middleware 字段