mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
fix(nuxt): show client error if no page matches after validate fails (#18978)
This commit is contained in:
parent
4b2901bee1
commit
7d0ecb5a96
@ -1,11 +1,38 @@
|
|||||||
import { defineNuxtRouteMiddleware } from '#app/composables/router'
|
import { createError, showError } from '#app/composables/error'
|
||||||
|
import { callWithNuxt, useNuxtApp } from '#app/nuxt'
|
||||||
|
import { defineNuxtRouteMiddleware, useRouter } from '#app/composables/router'
|
||||||
|
|
||||||
export default defineNuxtRouteMiddleware(async (to) => {
|
export default defineNuxtRouteMiddleware(async (to) => {
|
||||||
if (!to.meta?.validate) { return }
|
if (!to.meta?.validate) { return }
|
||||||
|
|
||||||
|
const nuxtApp = useNuxtApp()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const result = await Promise.resolve(to.meta.validate(to))
|
const result = await Promise.resolve(to.meta.validate(to))
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return result
|
if (process.server) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
const error = createError({
|
||||||
|
statusCode: 404,
|
||||||
|
statusMessage: `Page Not Found: ${to.fullPath}`
|
||||||
|
})
|
||||||
|
const unsub = router.beforeResolve((final) => {
|
||||||
|
unsub()
|
||||||
|
if (final === to) {
|
||||||
|
const unsub = router.afterEach(async () => {
|
||||||
|
unsub()
|
||||||
|
await callWithNuxt(nuxtApp, showError, [error])
|
||||||
|
// We pretend to have navigated to the invalid route so
|
||||||
|
// that the user can return to the previous page with
|
||||||
|
// the back button.
|
||||||
|
window.history.pushState({}, '', to.fullPath)
|
||||||
|
})
|
||||||
|
// We stop the navigation immediately before it resolves
|
||||||
|
// if there is no other route matching it.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -98,6 +98,16 @@ describe('pages', () => {
|
|||||||
it('validates routes', async () => {
|
it('validates routes', async () => {
|
||||||
const { status } = await fetch('/forbidden')
|
const { status } = await fetch('/forbidden')
|
||||||
expect(status).toEqual(404)
|
expect(status).toEqual(404)
|
||||||
|
|
||||||
|
const page = await createPage('/navigate-to-forbidden')
|
||||||
|
await page.waitForLoadState('networkidle')
|
||||||
|
await page.getByText('should throw a 404 error').click()
|
||||||
|
expect(await page.getByRole('heading').textContent()).toMatchInlineSnapshot('"Page Not Found: /forbidden"')
|
||||||
|
|
||||||
|
page.goto(url('/navigate-to-forbidden'))
|
||||||
|
await page.waitForLoadState('networkidle')
|
||||||
|
await page.getByText('should be caught by catchall').click()
|
||||||
|
expect(await page.getByRole('heading').textContent()).toMatchInlineSnapshot('"[...slug].vue"')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('render 404', async () => {
|
it('render 404', async () => {
|
||||||
@ -107,7 +117,7 @@ describe('pages', () => {
|
|||||||
// expect(html).toMatchInlineSnapshot()
|
// expect(html).toMatchInlineSnapshot()
|
||||||
|
|
||||||
expect(html).toContain('[...slug].vue')
|
expect(html).toContain('[...slug].vue')
|
||||||
expect(html).toContain('404 at not-found')
|
expect(html).toContain('catchall at not-found')
|
||||||
|
|
||||||
// Middleware still runs after validation: https://github.com/nuxt/nuxt/issues/15650
|
// Middleware still runs after validation: https://github.com/nuxt/nuxt/issues/15650
|
||||||
expect(html).toContain('Middleware ran: true')
|
expect(html).toContain('Middleware ran: true')
|
||||||
@ -941,7 +951,7 @@ describe.runIf(isDev() && !isWebpack)('vite plugins', () => {
|
|||||||
expect(await $fetch('/__nuxt-test')).toBe('vite-plugin with __nuxt prefix')
|
expect(await $fetch('/__nuxt-test')).toBe('vite-plugin with __nuxt prefix')
|
||||||
})
|
})
|
||||||
it('does not allow direct access to nuxt source folder', async () => {
|
it('does not allow direct access to nuxt source folder', async () => {
|
||||||
expect(await $fetch('/app.config')).toContain('404')
|
expect(await $fetch('/app.config')).toContain('catchall at')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
15
test/fixtures/basic/error.vue
vendored
Normal file
15
test/fixtures/basic/error.vue
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<h1>{{ error?.message }}</h1>
|
||||||
|
This is the error page 😱
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { NuxtError } from '#app'
|
||||||
|
defineProps({
|
||||||
|
error: Object as () => NuxtError
|
||||||
|
})
|
||||||
|
</script>
|
4
test/fixtures/basic/pages/[...slug].vue
vendored
4
test/fixtures/basic/pages/[...slug].vue
vendored
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div>[...slug].vue</div>
|
<h1>[...slug].vue</h1>
|
||||||
<div>404 at {{ $route.params.slug[0] }}</div>
|
<div>catchall at {{ $route.params.slug[0] }}</div>
|
||||||
<div>Middleware ran: {{ !!($route.meta.override as any)?.includes('extended middleware') }}</div>
|
<div>Middleware ran: {{ !!($route.meta.override as any)?.includes('extended middleware') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
18
test/fixtures/basic/pages/navigate-to-forbidden.vue
vendored
Normal file
18
test/fixtures/basic/pages/navigate-to-forbidden.vue
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div>navigate-to-forbidden.vue</div>
|
||||||
|
<NuxtLink to="/forbidden">
|
||||||
|
should throw a 404 error
|
||||||
|
</NuxtLink>
|
||||||
|
<NuxtLink to="/some-404">
|
||||||
|
should be caught by catchall
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ['override'],
|
||||||
|
validate: to => to.path !== '/forbidden'
|
||||||
|
})
|
||||||
|
</script>
|
@ -70,7 +70,7 @@ if (process.env.TEST_ENV !== 'built' && !isWindows) {
|
|||||||
|
|
||||||
it('should detect new routes', async () => {
|
it('should detect new routes', async () => {
|
||||||
const html = await $fetch('/some-404')
|
const html = await $fetch('/some-404')
|
||||||
expect(html).toContain('404 at some-404')
|
expect(html).toContain('catchall at some-404')
|
||||||
|
|
||||||
// write new page route
|
// write new page route
|
||||||
const indexVue = await fsp.readFile(join(fixturePath, 'pages/index.vue'), 'utf8')
|
const indexVue = await fsp.readFile(join(fixturePath, 'pages/index.vue'), 'utf8')
|
||||||
|
Loading…
Reference in New Issue
Block a user