mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
fix: handle errors that are not error instances (#4321)
This commit is contained in:
parent
846455e2f7
commit
9fbd581557
@ -6,19 +6,21 @@ import Youch from '@nuxtjs/youch'
|
|||||||
|
|
||||||
export default ({ resources, options }) => function errorMiddleware(err, req, res, next) {
|
export default ({ resources, options }) => function errorMiddleware(err, req, res, next) {
|
||||||
// ensure statusCode, message and name fields
|
// ensure statusCode, message and name fields
|
||||||
err.statusCode = err.statusCode || 500
|
|
||||||
err.message = err.message || 'Nuxt Server Error'
|
|
||||||
err.name = !err.name || err.name === 'Error' ? 'NuxtServerError' : err.name
|
|
||||||
|
|
||||||
// We hide actual errors from end users, so show them on server logs
|
const error = {
|
||||||
if (err.statusCode !== 404) {
|
statusCode: err.statusCode || 500,
|
||||||
consola.error(err)
|
message: err.message || 'Nuxt Server Error',
|
||||||
|
name: !err.name || err.name === 'Error' ? 'NuxtServerError' : err.name
|
||||||
}
|
}
|
||||||
|
const errorFull = err instanceof Error ? err : typeof err === 'string'
|
||||||
|
? new Error(err) : new Error(err.message || JSON.stringify(err))
|
||||||
|
errorFull.name = error.name
|
||||||
|
errorFull.statusCode = error.statusCode
|
||||||
|
|
||||||
const sendResponse = (content, type = 'text/html') => {
|
const sendResponse = (content, type = 'text/html') => {
|
||||||
// Set Headers
|
// Set Headers
|
||||||
res.statusCode = err.statusCode
|
res.statusCode = error.statusCode
|
||||||
res.statusMessage = err.name
|
res.statusMessage = error.name
|
||||||
res.setHeader('Content-Type', type + '; charset=utf-8')
|
res.setHeader('Content-Type', type + '; charset=utf-8')
|
||||||
res.setHeader('Content-Length', Buffer.byteLength(content))
|
res.setHeader('Content-Length', Buffer.byteLength(content))
|
||||||
res.setHeader('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
|
res.setHeader('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
|
||||||
@ -36,11 +38,15 @@ export default ({ resources, options }) => function errorMiddleware(err, req, re
|
|||||||
|
|
||||||
// Use basic errors when debug mode is disabled
|
// Use basic errors when debug mode is disabled
|
||||||
if (!options.debug) {
|
if (!options.debug) {
|
||||||
|
// We hide actual errors from end users, so show them on server logs
|
||||||
|
if (err.statusCode !== 404) {
|
||||||
|
consola.error(err)
|
||||||
|
}
|
||||||
// Json format is compatible with Youch json responses
|
// Json format is compatible with Youch json responses
|
||||||
const json = {
|
const json = {
|
||||||
status: err.statusCode,
|
status: error.statusCode,
|
||||||
message: err.message,
|
message: error.message,
|
||||||
name: err.name
|
name: error.name
|
||||||
}
|
}
|
||||||
if (isJson) {
|
if (isJson) {
|
||||||
sendResponse(JSON.stringify(json, undefined, 2), 'text/json')
|
sendResponse(JSON.stringify(json, undefined, 2), 'text/json')
|
||||||
@ -53,7 +59,7 @@ export default ({ resources, options }) => function errorMiddleware(err, req, re
|
|||||||
|
|
||||||
// Show stack trace
|
// Show stack trace
|
||||||
const youch = new Youch(
|
const youch = new Youch(
|
||||||
err,
|
errorFull,
|
||||||
req,
|
req,
|
||||||
readSourceFactory({
|
readSourceFactory({
|
||||||
srcDir: options.srcDir,
|
srcDir: options.srcDir,
|
||||||
|
@ -36,11 +36,6 @@ Object.assign(Vue.config, <%= serialize(vue.config) %>)<%= isTest ? '// eslint-d
|
|||||||
if (!Vue.config.$nuxt) {
|
if (!Vue.config.$nuxt) {
|
||||||
const defaultErrorHandler = Vue.config.errorHandler
|
const defaultErrorHandler = Vue.config.errorHandler
|
||||||
Vue.config.errorHandler = (err, vm, info, ...rest) => {
|
Vue.config.errorHandler = (err, vm, info, ...rest) => {
|
||||||
const nuxtError = {
|
|
||||||
statusCode: err.statusCode || err.name || 'Whoops!',
|
|
||||||
message: err.message || err.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call other handler if exist
|
// Call other handler if exist
|
||||||
let handled = null
|
let handled = null
|
||||||
if (typeof defaultErrorHandler === 'function') {
|
if (typeof defaultErrorHandler === 'function') {
|
||||||
@ -56,7 +51,7 @@ if (!Vue.config.$nuxt) {
|
|||||||
|
|
||||||
// Show Nuxt Error Page
|
// Show Nuxt Error Page
|
||||||
if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
|
if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
|
||||||
vm.$root[nuxtApp].error(nuxtError)
|
vm.$root[nuxtApp].error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +63,7 @@ if (!Vue.config.$nuxt) {
|
|||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
} else {
|
} else {
|
||||||
console.error(err.message || nuxtError.message)
|
console.error(err.message || err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Vue.config.$nuxt = {}
|
Vue.config.$nuxt = {}
|
||||||
@ -150,9 +145,7 @@ async function loadAsyncComponents(to, from, next) {
|
|||||||
// Call next()
|
// Call next()
|
||||||
next()
|
next()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = err || {}
|
this.error(err)
|
||||||
const statusCode = (error.statusCode || error.status || (error.response && error.response.status) || 500)
|
|
||||||
this.error({ statusCode, message: error.message })
|
|
||||||
this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
|
this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
|
||||||
next(false)
|
next(false)
|
||||||
}
|
}
|
||||||
@ -414,8 +407,6 @@ async function render(to, from, next) {
|
|||||||
return this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
|
return this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
|
||||||
}
|
}
|
||||||
_lastPaths = []
|
_lastPaths = []
|
||||||
const errorResponseStatus = (error.response && error.response.status)
|
|
||||||
error.statusCode = error.statusCode || error.status || errorResponseStatus || 500
|
|
||||||
|
|
||||||
globalHandleError(error)
|
globalHandleError(error)
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import NuxtLink from './components/nuxt-link.js'
|
|||||||
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
|
import NuxtError from '<%= components.ErrorPage ? components.ErrorPage : "./components/nuxt-error.vue" %>'
|
||||||
import Nuxt from './components/nuxt.js'
|
import Nuxt from './components/nuxt.js'
|
||||||
import App from '<%= appPath %>'
|
import App from '<%= appPath %>'
|
||||||
import { setContext, getLocation, getRouteData } from './utils'
|
import { setContext, getLocation, getRouteData, normalizeError } from './utils'
|
||||||
<% if (store) { %>import { createStore } from './store.js'<% } %>
|
<% if (store) { %>import { createStore } from './store.js'<% } %>
|
||||||
|
|
||||||
/* Plugins */
|
/* Plugins */
|
||||||
@ -90,7 +90,7 @@ async function createApp(ssrContext) {
|
|||||||
error(err) {
|
error(err) {
|
||||||
err = err || null
|
err = err || null
|
||||||
app.context._errored = !!err
|
app.context._errored = !!err
|
||||||
if (typeof err === 'string') err = { statusCode: 500, message: err }
|
err = err ? normalizeError(err) : null
|
||||||
const nuxt = this.nuxt || this.$options.nuxt
|
const nuxt = this.nuxt || this.$options.nuxt
|
||||||
nuxt.dateErr = Date.now()
|
nuxt.dateErr = Date.now()
|
||||||
nuxt.err = err
|
nuxt.err = err
|
||||||
|
@ -273,6 +273,23 @@ export function getQueryDiff(toQuery, fromQuery) {
|
|||||||
return diff
|
return diff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeError(err) {
|
||||||
|
let message
|
||||||
|
if (!(err.message || typeof err === 'string')) {
|
||||||
|
try {
|
||||||
|
message = JSON.stringify(err, null, 2)
|
||||||
|
} catch (e) {
|
||||||
|
message = `[${err.constructor.name}]`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message = err.message || err
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
message: message,
|
||||||
|
statusCode: (err.statusCode || err.status || (err.response && err.response.status) || 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main path matching regexp utility.
|
* The main path matching regexp utility.
|
||||||
*
|
*
|
||||||
@ -548,3 +565,4 @@ function formatQuery(query) {
|
|||||||
return key + '=' + val
|
return key + '=' + val
|
||||||
}).filter(Boolean).join('&')
|
}).filter(Boolean).join('&')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ describe('basic browser', () => {
|
|||||||
test('/error', async () => {
|
test('/error', async () => {
|
||||||
await page.nuxt.navigate('/error')
|
await page.nuxt.navigate('/error')
|
||||||
|
|
||||||
expect(await page.nuxt.errorData()).toEqual({ statusCode: 500 })
|
expect(await page.nuxt.errorData()).toEqual({ message: 'Error mouahahah', statusCode: 500 })
|
||||||
expect(await page.$text('.title')).toBe('Error mouahahah')
|
expect(await page.$text('.title')).toBe('Error mouahahah')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ describe('basic browser', () => {
|
|||||||
await page.nuxt.navigate('/error2')
|
await page.nuxt.navigate('/error2')
|
||||||
|
|
||||||
expect(await page.$text('.title')).toBe('Custom error')
|
expect(await page.$text('.title')).toBe('Custom error')
|
||||||
expect(await page.nuxt.errorData()).toEqual({ message: 'Custom error' })
|
expect(await page.nuxt.errorData()).toEqual({ message: 'Custom error', statusCode: 500 })
|
||||||
})
|
})
|
||||||
|
|
||||||
test('/redirect-middleware', async () => {
|
test('/redirect-middleware', async () => {
|
||||||
|
8
test/fixtures/basic/pages/error-object.vue
vendored
Normal file
8
test/fixtures/basic/pages/error-object.vue
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fetch() {
|
||||||
|
throw { error: 'fetch error!' } // eslint-disable-line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
8
test/fixtures/basic/pages/error-string.vue
vendored
Normal file
8
test/fixtures/basic/pages/error-string.vue
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fetch() {
|
||||||
|
throw 'fetch error!' // eslint-disable-line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
2
test/fixtures/basic/pages/error.vue
vendored
2
test/fixtures/basic/pages/error.vue
vendored
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
asyncData({ req }) {
|
asyncData() {
|
||||||
throw new Error('Error mouahahah')
|
throw new Error('Error mouahahah')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
test/fixtures/spa/pages/error-handler-object.vue
vendored
Normal file
8
test/fixtures/spa/pages/error-handler-object.vue
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fetch() {
|
||||||
|
throw { error: 'fetch error!' } // eslint-disable-line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
8
test/fixtures/spa/pages/error-handler-string.vue
vendored
Normal file
8
test/fixtures/spa/pages/error-handler-string.vue
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fetch() {
|
||||||
|
throw 'fetch error!' // eslint-disable-line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
2
test/fixtures/spa/plugins/error.js
vendored
2
test/fixtures/spa/plugins/error.js
vendored
@ -1,5 +1,5 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
|
||||||
Vue.config.errorHandler = function (err) {
|
Vue.config.errorHandler = function (err) {
|
||||||
document.body.appendChild(document.createTextNode(`error handler triggered: ${err.message}`))
|
document.body.appendChild(document.createTextNode(`error handler triggered: ${err.message || err}`))
|
||||||
}
|
}
|
||||||
|
@ -174,6 +174,26 @@ describe('basic ssr', () => {
|
|||||||
.rejects.toThrow('Error mouahahah')
|
.rejects.toThrow('Error mouahahah')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('/error-string', async () => {
|
||||||
|
let error
|
||||||
|
try {
|
||||||
|
await nuxt.server.renderRoute('/error-string', { req: {}, res: {} })
|
||||||
|
} catch (e) {
|
||||||
|
error = e
|
||||||
|
}
|
||||||
|
await expect(error).toEqual('fetch error!')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/error-object', async () => {
|
||||||
|
let error
|
||||||
|
try {
|
||||||
|
await nuxt.server.renderRoute('/error-object', { req: {}, res: {} })
|
||||||
|
} catch (e) {
|
||||||
|
error = e
|
||||||
|
}
|
||||||
|
await expect(error).toEqual({ error: 'fetch error!' })
|
||||||
|
})
|
||||||
|
|
||||||
test('/error status code', async () => {
|
test('/error status code', async () => {
|
||||||
await expect(rp(url('/error'))).rejects.toMatchObject({
|
await expect(rp(url('/error'))).rejects.toMatchObject({
|
||||||
statusCode: 500
|
statusCode: 500
|
||||||
@ -201,7 +221,7 @@ describe('basic ssr', () => {
|
|||||||
const { html, error } = await nuxt.server.renderRoute('/error2')
|
const { html, error } = await nuxt.server.renderRoute('/error2')
|
||||||
expect(html).toContain('Custom error')
|
expect(html).toContain('Custom error')
|
||||||
expect(error.message).toContain('Custom error')
|
expect(error.message).toContain('Custom error')
|
||||||
expect(error.statusCode === undefined).toBe(true)
|
expect(error.statusCode).toBe(500)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('/error2 status code', async () => {
|
test('/error2 status code', async () => {
|
||||||
|
@ -41,13 +41,21 @@ describe('spa', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('/error-handler', async () => {
|
test('/error-handler', async () => {
|
||||||
await renderRoute('/error-handler')
|
|
||||||
const { html } = await renderRoute('/error-handler')
|
const { html } = await renderRoute('/error-handler')
|
||||||
expect(html).toMatch('error handler triggered: fetch error!')
|
expect(html).toMatch('error handler triggered: fetch error!')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('/error-handler-object', async () => {
|
||||||
|
const { html } = await renderRoute('/error-handler')
|
||||||
|
expect(html).toMatch('error handler triggered: fetch error!')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/error-handler-string', async () => {
|
||||||
|
const { html } = await renderRoute('/error-handler-string')
|
||||||
|
expect(html).toMatch('error handler triggered: fetch error!')
|
||||||
|
})
|
||||||
|
|
||||||
test('/error-handler-async', async () => {
|
test('/error-handler-async', async () => {
|
||||||
await renderRoute('/error-handler-async')
|
|
||||||
const { html } = await renderRoute('/error-handler-async')
|
const { html } = await renderRoute('/error-handler-async')
|
||||||
expect(html).toMatch('error handler triggered: asyncData error!')
|
expect(html).toMatch('error handler triggered: asyncData error!')
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user