mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 00:23:53 +00:00
fix(nuxt): ensure useError
is called with nuxt app context (#20585)
This commit is contained in:
parent
675445f98a
commit
16bf228437
@ -314,13 +314,13 @@ Be very careful before proxying headers to an external API and just include head
|
||||
If you want to pass on/proxy cookies in the other direction, from an internal request back to the client, you will need to handle this yourself.
|
||||
|
||||
```ts [composables/fetch.ts]
|
||||
import { appendHeader, H3Event } from 'h3'
|
||||
import { appendResponseHeader, H3Event } from 'h3'
|
||||
|
||||
export const fetchWithCookie = async (event: H3Event, url: string) => {
|
||||
const res = await $fetch.raw(url)
|
||||
const cookies = (res.headers.get('set-cookie') || '').split(',')
|
||||
for (const cookie of cookies) {
|
||||
appendHeader(event, 'set-cookie', cookie)
|
||||
appendResponseHeader(event, 'set-cookie', cookie)
|
||||
}
|
||||
return res._data
|
||||
}
|
||||
|
@ -67,6 +67,10 @@ When you are ready to remove the error page, you can call the `clearError` helpe
|
||||
Make sure to check before using anything dependent on Nuxt plugins, such as `$route` or `useRouter`, as if a plugin threw an error, then it won't be re-run until you clear the error.
|
||||
::
|
||||
|
||||
::alert{type="warning"}
|
||||
If you are running on Node 16 and you set any cookies when rendering your error page, they will [overwrite cookies previously set](https://github.com/nuxt/nuxt/pull/20585). We recommend using a newer version of Node as Node 16 will reach end-of-life in September 2023.
|
||||
::
|
||||
|
||||
### Example
|
||||
|
||||
```vue [error.vue]
|
||||
|
@ -2,7 +2,7 @@ import type { RendererNode } from 'vue'
|
||||
import { computed, createStaticVNode, defineComponent, getCurrentInstance, h, ref, watch } from 'vue'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import { hash } from 'ohash'
|
||||
import { appendHeader } from 'h3'
|
||||
import { appendResponseHeader } from 'h3'
|
||||
import { useHead } from '@unhead/vue'
|
||||
|
||||
// eslint-disable-next-line import/no-restricted-paths
|
||||
@ -42,7 +42,7 @@ export default defineComponent({
|
||||
const url = `/__nuxt_island/${props.name}:${hashId.value}`
|
||||
if (process.server && process.env.prerender) {
|
||||
// Hint to Nitro to prerender the island component
|
||||
appendHeader(event, 'x-nitro-prerender', url)
|
||||
appendResponseHeader(event, 'x-nitro-prerender', url)
|
||||
}
|
||||
// TODO: Validate response
|
||||
return $fetch<NuxtIslandResponse>(url, {
|
||||
|
@ -2,7 +2,7 @@ import type { Ref } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import type { CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
|
||||
import { parse, serialize } from 'cookie-es'
|
||||
import { appendHeader } from 'h3'
|
||||
import { appendResponseHeader } from 'h3'
|
||||
import type { H3Event } from 'h3'
|
||||
import destr from 'destr'
|
||||
import { isEqual } from 'ohash'
|
||||
@ -48,11 +48,10 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
|
||||
}
|
||||
}
|
||||
const unhook = nuxtApp.hooks.hookOnce('app:rendered', writeFinalCookieValue)
|
||||
const writeAndUnhook = () => {
|
||||
nuxtApp.hooks.hookOnce('app:error', () => {
|
||||
unhook() // don't write cookie subsequently when app:rendered is called
|
||||
return writeFinalCookieValue()
|
||||
}
|
||||
nuxtApp.hooks.hookOnce('app:error', writeAndUnhook)
|
||||
})
|
||||
}
|
||||
|
||||
return cookie as CookieRef<T>
|
||||
@ -82,6 +81,6 @@ function writeClientCookie (name: string, value: any, opts: CookieSerializeOptio
|
||||
function writeServerCookie (event: H3Event, name: string, value: any, opts: CookieSerializeOptions = {}) {
|
||||
if (event) {
|
||||
// TODO: Try to smart join with existing Set-Cookie headers
|
||||
appendHeader(event, 'Set-Cookie', serializeCookie(name, value, opts))
|
||||
appendResponseHeader(event, 'Set-Cookie', serializeCookie(name, value, opts))
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,10 @@ export const showError = (_err: string | Error | Partial<NuxtError>) => {
|
||||
|
||||
try {
|
||||
const nuxtApp = useNuxtApp()
|
||||
nuxtApp.callHook('app:error', err)
|
||||
const error = useError()
|
||||
if (process.client) {
|
||||
nuxtApp.hooks.callHook('app:error', err)
|
||||
}
|
||||
error.value = error.value || err
|
||||
} catch {
|
||||
throw err
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Fragment, computed, createStaticVNode, createVNode, defineComponent, h, ref, watch } from 'vue'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import { hash } from 'ohash'
|
||||
import { appendHeader } from 'h3'
|
||||
import { appendResponseHeader } from 'h3'
|
||||
|
||||
import { useHead } from '@unhead/vue'
|
||||
import type { NuxtIslandResponse } from '../../core/runtime/nitro/renderer'
|
||||
@ -51,7 +51,7 @@ const NuxtServerComponent = defineComponent({
|
||||
const url = `/__nuxt_island/${props.name}:${hashId.value}`
|
||||
if (process.server && process.env.prerender) {
|
||||
// Hint to Nitro to prerender the island component
|
||||
appendHeader(event, 'x-nitro-prerender', url)
|
||||
appendResponseHeader(event, 'x-nitro-prerender', url)
|
||||
}
|
||||
// TODO: Validate response
|
||||
return $fetch<NuxtIslandResponse>(url, {
|
||||
|
@ -2,7 +2,7 @@ import { createRenderer, renderResourceHeaders } from 'vue-bundle-renderer/runti
|
||||
import type { RenderResponse } from 'nitropack'
|
||||
import type { Manifest } from 'vite'
|
||||
import type { H3Event } from 'h3'
|
||||
import { appendHeader, createError, getQuery, readBody, writeEarlyHints } from 'h3'
|
||||
import { appendResponseHeader, createError, getQuery, readBody, writeEarlyHints } from 'h3'
|
||||
import devalue from '@nuxt/devalue'
|
||||
import { stringify, uneval } from 'devalue'
|
||||
import destr from 'destr'
|
||||
@ -275,7 +275,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
|
||||
if (_PAYLOAD_EXTRACTION) {
|
||||
// Hint nitro to prerender payload for this route
|
||||
appendHeader(event, 'x-nitro-prerender', joinURL(url, process.env.NUXT_JSON_PAYLOADS ? '_payload.json' : '_payload.js'))
|
||||
appendResponseHeader(event, 'x-nitro-prerender', joinURL(url, process.env.NUXT_JSON_PAYLOADS ? '_payload.json' : '_payload.js'))
|
||||
// Use same ssr context to generate payload for this route
|
||||
PAYLOAD_CACHE!.set(withoutTrailingSlash(url), renderPayloadResponse(ssrContext))
|
||||
}
|
||||
|
@ -114,8 +114,9 @@ describe('pages', () => {
|
||||
})
|
||||
|
||||
it('validates routes', async () => {
|
||||
const { status } = await fetch('/forbidden')
|
||||
const { status, headers } = await fetch('/forbidden')
|
||||
expect(status).toEqual(404)
|
||||
expect(headers.get('Set-Cookie')).toBe('set-in-plugin=true; Path=/')
|
||||
|
||||
const page = await createPage('/navigate-to-forbidden')
|
||||
await page.waitForLoadState('networkidle')
|
||||
@ -135,8 +136,11 @@ describe('pages', () => {
|
||||
expect(status).toEqual(500)
|
||||
})
|
||||
|
||||
it('render 404', async () => {
|
||||
const html = await $fetch('/not-found')
|
||||
it('render catchall page', async () => {
|
||||
const res = await fetch('/not-found')
|
||||
expect(res.status).toEqual(200)
|
||||
|
||||
const html = await res.text()
|
||||
|
||||
// Snapshot
|
||||
// expect(html).toMatchInlineSnapshot()
|
||||
@ -578,7 +582,9 @@ describe('errors', () => {
|
||||
|
||||
it('should render a HTML error page', async () => {
|
||||
const res = await fetch('/error')
|
||||
expect(res.headers.get('Set-Cookie')).toBe('some-error=was%20set; Path=/')
|
||||
expect(res.headers.get('Set-Cookie')).toBe('set-in-plugin=true; Path=/')
|
||||
// TODO: enable when we update test to node v16
|
||||
// expect(res.headers.get('Set-Cookie')).toBe('set-in-plugin=true; Path=/, some-error=was%20set; Path=/')
|
||||
expect(await res.text()).toContain('This is a custom error')
|
||||
})
|
||||
|
||||
|
3
test/fixtures/basic/plugins/cookie.ts
vendored
Normal file
3
test/fixtures/basic/plugins/cookie.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
export default defineNuxtPlugin(() => {
|
||||
useCookie('set-in-plugin').value = 'true'
|
||||
})
|
Loading…
Reference in New Issue
Block a user