diff --git a/lib/app/components/nuxt.js b/lib/app/components/nuxt.js index 243eec2023..66f15a686d 100644 --- a/lib/app/components/nuxt.js +++ b/lib/app/components/nuxt.js @@ -7,6 +7,9 @@ export default { name: 'nuxt', props: ['nuxtChildKey'], render(h) { + if (this.nuxt._redirected) { + return h('div', [ 'redirecting.' ]) + } // If there is some error if (this.nuxt.err) { return h('nuxt-error', { diff --git a/lib/app/utils.js b/lib/app/utils.js index f2a707f0f3..297c2df480 100644 --- a/lib/app/utils.js +++ b/lib/app/utils.js @@ -132,11 +132,23 @@ export async function setContext(app, context) { path = status status = 302 } - app.context.next({ - path: path, - query: query, - status: status - }) + if (path.charAt(0) === '/' && path.charAt(1) !== '/') { + app.context.next({ + path: path, + query: query, + status: status + }) + } else { + path = formatUrl(path, query) + if (process.server) { + app.context.res.setHeader('Location', path) + app.context.res.statusCode = status + app.nuxt._redirected = true + } + if (process.client) { + window.location = path + } + } } if (process.server) app.context.beforeNuxtRender = (fn) => context.beforeRenderFns.push(fn) if (process.client) app.context.nuxtState = window.__NUXT__ @@ -442,3 +454,60 @@ function escapeString(str) { function escapeGroup(group) { return group.replace(/([=!:$\/()])/g, '\\$1') } + +/** + * Format given url, append query to url query string + * + * @param {string} url + * @param {string} query + * @return {string} + */ +function formatUrl (url, query) { + let protocol + let index = url.indexOf('://') + if (index !== -1) { + protocol = url.substring(0, index) + url = url.substring(index + 3) + } else if (url.indexOf('//') === 0) { + url = url.substring(2) + } + + let parts = url.split('/') + let result = (protocol ? protocol + '://' : '//') + parts.shift() + + let path = parts.filter(Boolean).join('/') + let hash + parts = path.split('#') + if (parts.length === 2) { + path = parts[0] + hash = parts[1] + } + + result += path ? '/' + path : '' + + if (query && JSON.stringify(query) !== '{}') { + result += (url.split('?').length === 2 ? '&' : '?') + formatQuery(query) + } + result += hash ? '#' + hash : '' + + return result +} + +/** + * Transform data object to query string + * + * @param {object} query + * @return {string} + */ +function formatQuery (query) { + return Object.keys(query).sort().map(key => { + var val = query[key] + if (val == null) { + return '' + } + if (Array.isArray(val)) { + return val.slice().map(val2 => [key, '=', val2].join('')).join('&') + } + return key + '=' + val + }).filter(Boolean).join('&') +} diff --git a/test/basic.csr.test.js b/test/basic.csr.test.js index da09e77a1b..3c58c37462 100644 --- a/test/basic.csr.test.js +++ b/test/basic.csr.test.js @@ -143,6 +143,15 @@ test('/redirect2', async t => { t.is(await page.$text('h1'), 'Index page') }) +test('/redirect3', async t => { + // New page for redirecting to external link. + const page = await browser.page(url('/')) + await page.nuxt.navigate('/redirect3', false) + await page.waitForFunction(() => window.location.href === 'https://nuxtjs.org/') + page.close() + t.pass() +}) + test('/no-ssr', async t => { await page.nuxt.navigate('/no-ssr') diff --git a/test/basic.ssr.test.js b/test/basic.ssr.test.js index e3dc4559b2..0ac2f77736 100755 --- a/test/basic.ssr.test.js +++ b/test/basic.ssr.test.js @@ -117,6 +117,19 @@ test('/redirect -> check redirected source', async t => { t.true(html.includes('

Index page

')) }) +test('/redirect -> external link', async t => { + const headers = {} + const { html } = await nuxt.renderRoute('/redirect3', { + res: { + setHeader(k, v) { + headers[k] = v + } + } + }) + t.is(headers.Location, 'https://nuxtjs.org') + t.true(html.includes('
redirecting.
')) +}) + test('/special-state -> check window.__NUXT__.test = true', async t => { const window = await nuxt.renderAndGetWindow(url('/special-state')) t.is(window.document.title, 'Nuxt.js') diff --git a/test/fixtures/basic/pages/redirect3.vue b/test/fixtures/basic/pages/redirect3.vue new file mode 100644 index 0000000000..1f587681d5 --- /dev/null +++ b/test/fixtures/basic/pages/redirect3.vue @@ -0,0 +1,11 @@ + + +