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) => {
|
||||
if (!to.meta?.validate) { return }
|
||||
|
||||
const nuxtApp = useNuxtApp()
|
||||
const router = useRouter()
|
||||
|
||||
const result = await Promise.resolve(to.meta.validate(to))
|
||||
if (result === true) {
|
||||
return
|
||||
}
|
||||
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 () => {
|
||||
const { status } = await fetch('/forbidden')
|
||||
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 () => {
|
||||
@ -107,7 +117,7 @@ describe('pages', () => {
|
||||
// expect(html).toMatchInlineSnapshot()
|
||||
|
||||
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
|
||||
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')
|
||||
})
|
||||
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>
|
||||
<div>
|
||||
<div>[...slug].vue</div>
|
||||
<div>404 at {{ $route.params.slug[0] }}</div>
|
||||
<h1>[...slug].vue</h1>
|
||||
<div>catchall at {{ $route.params.slug[0] }}</div>
|
||||
<div>Middleware ran: {{ !!($route.meta.override as any)?.includes('extended middleware') }}</div>
|
||||
</div>
|
||||
</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 () => {
|
||||
const html = await $fetch('/some-404')
|
||||
expect(html).toContain('404 at some-404')
|
||||
expect(html).toContain('catchall at some-404')
|
||||
|
||||
// write new page route
|
||||
const indexVue = await fsp.readFile(join(fixturePath, 'pages/index.vue'), 'utf8')
|
||||
|
Loading…
Reference in New Issue
Block a user