Nuxt 3,无法找到错误:在插件外部调用了需要访问 Nuxt 实例的可组合项

问题描述 投票:0回答:1

我有一个可组合项,它返回一个函数以在 5 秒的间隔内验证身份验证和刷新令牌。两者都利用 Nuxt 的

useCookie
实用函数来执行其背后的逻辑。

一开始,它有效。但有时,当令牌过期时,它会损坏并向我抛出错误屏幕上显示的错误。这是错误:


500

[nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a Nuxt bug. Find out more at `https://nuxt.com/docs/guide/concepts/auto-imports#using-vue-and-nuxt-composables`.

at Module.useAsyncData (./node_modules/nuxt/dist/app/composables/asyncData.js:26:38)
at useFetch (./node_modules/nuxt/dist/app/composables/fetch.js:53:43)
at refreshToken (./composables/useRepository.ts:206:35)
at verifyAuth (./composables/useRepository.ts:250:31)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async setup (./layouts/dashboard.js:35:87)

我已经多次阅读了我的代码实现,但找不到问题出在哪里。请帮我找出错误。

这是导致此错误发生的代码段。

useRepository.ts

export const useRepository = () => {
  const config = useRuntimeConfig()
  const cookie = useCookie<LoginResponse | null>('auth')

  const refreshToken = async () => {
    const { data, error } = await useFetch<RefreshTokenResponse>(
      `/api/jwt-auth/jwt/refresh/`,
      {
        baseURL: config.public.apiUrl,
        method: 'POST',
        query: { format: 'json' },
        body: { refresh: cookie.value?.refresh },
      },
    )

    if (error.value) {
      const { status } = error.value
      if (status === 400 || status === 401 || status === 403) {
        cookie.value = null
        navigateTo('/login?redirect=session-expired')
        return false
      }
    }

    if (!cookie.value) {
      console.warn(
        'You have issued a refresh token but no users has signed in to this session, check your code implementation.',
      )
      return false
    }

    if (!data.value || !data.value.access) {
      console.warn(
        'Server returns no access token, users will be forcefully kicked out to the login page. Please talk to your server administrator.',
      )
      return false
    }

    cookie.value = { ...cookie.value, access: data.value.access }
    return true
  }

  const verifyAuth = async (timer?: ReturnType<typeof setInterval>) => {
    const { error, execute: refetch } = await useFetch<VerifyAuthResponse>(
      `/api/jwt-auth/jwt/verify/`,
      {
        baseURL: config.public.apiUrl,
        method: 'POST',
        query: { format: 'json' },
        body: { token: cookie.value?.access },
      },
    )

    if (error.value) {
      const data = await error.value.data
      if (data && data.code === 'token_not_valid') {
        const success = await refreshToken()
        if (!success) {
          if (timer) {
            console.log('clearing auth cookie')
            clearInterval(timer)
          }
          cookie.value = null
          return navigateTo('/login?redirect=session-expired')
        } else {
          console.log('refetching')
          await refetch()
        }
      }
    }
  }

  return {
    verifyAuth,
    refreshToken,
  }
}

dashboard.vue

<script setup lang="ts">
useHead({
  htmlAttrs: {
    lang: 'id',
  },
  titleTemplate(title) {
    return title
      ? `${title} - MyApp`
      : 'My Branding Punchline - MyApp'
  },
  bodyAttrs: {
    class:
      'scrollbar scrollbar-thumb-primary scrollbar-track-primary/10 scrollbar-thin scroll-smooth',
  },
})

const { verifyAuth } = useRepository()
await verifyAuth()

const timer = ref<ReturnType<typeof setInterval>>()
const stopVerifyingAuth = () => {
  if (timer.value) clearInterval(timer.value)
}

const continuouslyVerifyAuth = () => {
  timer.value = setInterval(() => {
    verifyAuth(timer.value)
  }, 5000)
}

onActivated(() => continuouslyVerifyAuth())
onDeactivated(() => stopVerifyingAuth())
onUnmounted(() => stopVerifyingAuth())

const windowFocus = useWindowFocus()
watch(windowFocus, async (isFocus) => {
  if (isFocus) {
    await verifyAuth(timer.value)
    continuouslyVerifyAuth()
  } else {
    stopVerifyingAuth()
  }
})
</script>

<template>
  <main class="box-border antialiased text-black overflow-x-hidden">
    <div class="h-screen flex items-start">
      <TheSidebar />
      <div
        class="w-full h-full overflow-y-auto scrollbar-thumb-primary scrollbar-track-primary/10 scrollbar-thin scroll-smooth"
      >
        <slot />
      </div>
    </div>
  </main>
</template>

nuxt.js nuxtjs3
1个回答
0
投票

经过几天的调试,事实证明我的实现不应该放在

layouts
文件中。

我不知道为什么。

我当前的解决方案是创建一个在每个页面上使用的包装组件。

所以现在我的代码看起来像这样

layouts/dashboard.vue

<script setup lang="ts">
useHead({
  htmlAttrs: {
    lang: 'id',
  },
  titleTemplate(title) {
    return title
      ? `${title} - MyApp`
      : 'My Branding Punchline - MyApp'
  },
  bodyAttrs: {
    class:
      'scrollbar scrollbar-thumb-primary scrollbar-track-primary/10 scrollbar-thin scroll-smooth',
  },
})
</script>

<template>
  <main class="box-border antialiased text-black overflow-x-hidden">
    <div class="h-screen flex items-start">
      <TheSidebar />
      <div
        class="w-full h-full overflow-y-auto scrollbar-thumb-primary scrollbar-track-primary/10 scrollbar-thin scroll-smooth"
      >
        <slot />
      </div>
    </div>
  </main>
</template>

components/DashboardContent.vue

<script setup lang="ts">
const { verifyAuth } = useRepository()
await verifyAuth()

const timer = ref<ReturnType<typeof setInterval>>()
const stopVerifyingAuth = () => {
  if (timer.value) clearInterval(timer.value)
}

const continuouslyVerifyAuth = () => {
  timer.value = setInterval(() => {
    verifyAuth(timer.value)
  }, 5000)
}

onActivated(() => continuouslyVerifyAuth())
onDeactivated(() => stopVerifyingAuth())
onUnmounted(() => stopVerifyingAuth())

const windowFocus = useWindowFocus()
watch(windowFocus, async (isFocus) => {
  if (isFocus) {
    await verifyAuth(timer.value)
    continuouslyVerifyAuth()
  } else {
    stopVerifyingAuth()
  }
})
</script>

<template>
  <div>
    <slot />
  </div>
</template>

pages/dashboard/index.vue

<script setup lang="ts">
  definePageMeta({ middleware: ['auth'], layout: 'dashboard' })
</script>

<template>
  <DashboardContent>
    Hello World!
  </DashboardContent>
</template>
© www.soinside.com 2019 - 2024. All rights reserved.