fix: handle route encoding (#8325)

Co-authored-by: farnabaz <farnabaz@users.noreply.github.com>
This commit is contained in:
pooya parsa 2020-11-30 23:10:02 +01:00 committed by GitHub
parent 7cac3c7fc9
commit cc1f6d94b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 91 additions and 86 deletions

View File

@ -63,7 +63,7 @@
"vue-client-only": "^2.0.0",
"vue-meta": "^2.4.0",
"vue-no-ssr": "^1.1.1",
"vue-router": "3.4.8",
"vue-router": "^3.4.9",
"vuex": "^3.5.1"
},
"engines": {

View File

@ -126,6 +126,7 @@ export function getNuxtConfig (_options) {
if (!/\/$/.test(options.router.base)) {
options.router.base += '/'
}
options.router.base = encodeURI(decodeURI(options.router.base))
// Legacy support for export
if (options.export) {

View File

@ -50,6 +50,7 @@ export default class Listener {
}
this.port = address.port
this.url = `http${this.https ? 's' : ''}://${this.host}:${this.port}${this.baseURL}`
this.url = decodeURI(this.url)
return
}
this.url = `unix+http://${address}`

View File

@ -167,7 +167,7 @@ export default class Server {
prefix: false,
handler: (req, res) => {
const to = urlJoin(this.nuxt.options.router.base, req.url)
consola.info(`[Development] Redirecting from \`${req.url}\` to \`${to}\` (router.base specified).`)
consola.info(`[Development] Redirecting from \`${decodeURI(req.url)}\` to \`${decodeURI(to)}\` (router.base specified).`)
res.writeHead(302, {
Location: to
})

View File

@ -19,7 +19,7 @@
"vue-client-only": "^2.0.0",
"vue-meta": "^2.4.0",
"vue-no-ssr": "^1.1.1",
"vue-router": "3.4.8",
"vue-router": "^3.4.9",
"vue-template-compiler": "^2.6.12",
"vuex": "^3.5.1"
},

View File

@ -47,7 +47,7 @@ import scrollBehavior from './router.scrollBehavior.js'
}
// @see: https://router.vuejs.org/api/#router-construction-options
res += '{'
res += firstIndent + 'path: ' + JSON.stringify(route.path)
res += firstIndent + 'path: ' + JSON.stringify(encodeURI(decodeURI(route.path)))
res += (route.components) ? nextIndent + 'components: {' + resMap + '\n' + baseIndent + tab + '}' : ''
res += (route.component) ? nextIndent + 'component: ' + route._name : ''
res += (route.redirect) ? nextIndent + 'redirect: ' + JSON.stringify(route.redirect) : ''
@ -93,7 +93,7 @@ Vue.use(Router)
export const routerOptions = {
mode: '<%= router.mode %>',
base: decodeURI('<%= router.base %>'),
base: '<%= router.base %>',
linkActiveClass: '<%= router.linkActiveClass %>',
linkExactActiveClass: '<%= router.linkExactActiveClass %>',
scrollBehavior,
@ -106,5 +106,16 @@ export const routerOptions = {
}
export function createRouter () {
return new Router(routerOptions)
const router = new Router(routerOptions)
const resolve = router.resolve.bind(router)
// encodeURI(decodeURI()) ~> support both encoded and non-encoded urls
router.resolve = (to, current, append) => {
if (typeof to === 'string') {
to = encodeURI(decodeURI(to))
}
return resolve(to, current, append)
}
return router
}

View File

@ -7,7 +7,8 @@ import {
<% if (features.middleware) { %>middlewareSeries,<% } %>
<% if (features.middleware && features.layouts) { %>sanitizeComponent,<% } %>
getMatchedComponents,
promisify
promisify,
ensureURIEncoded
} from './utils.js'
<% if (features.fetch) { %>import fetchMixin from './mixins/fetch.server'<% } %>
import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
@ -50,7 +51,7 @@ const createNext = ssrContext => (opts) => {
opts.path = urlJoin(routerBase, opts.path)
}
// Avoid loop redirect
if (decodeURIComponent(opts.path) === ssrContext.url) {
if (encodeURI(decodeURI(opts.path)) === ssrContext.url) {
ssrContext.redirected = false
return
}

View File

@ -296,15 +296,20 @@ export function promisify (fn, context) {
// Imported from vue-router
export function getLocation (base, mode) {
let path = decodeURI(window.location.pathname)
if (mode === 'hash') {
return window.location.hash.replace(/^#\//, '')
}
// To get matched with sanitized router.base add trailing slash
if (base && (path.endsWith('/') ? path : path + '/').startsWith(base)) {
base = decodeURI(base).slice(0, -1) // consideration is base is normalized with trailing slash
let path = decodeURI(window.location.pathname)
if (base && path.startsWith(base)) {
path = path.slice(base.length)
}
return (path || '/') + window.location.search + window.location.hash
const fullPath = (path || '/') + window.location.search + window.location.hash
return encodeURI(fullPath)
}
// Imported from path-to-regexp

View File

@ -274,7 +274,8 @@ export default class VueRenderer {
consola.debug(`Rendering url ${url}`)
// Add url to the renderContext
renderContext.url = url
renderContext.url = encodeURI(decodeURI(url))
// Add target to the renderContext
renderContext.target = this.options.target

View File

@ -5,9 +5,9 @@ const url = route => 'http://localhost:' + port + encodeURI(route)
let nuxt = null
describe('unicode-base', () => {
describe('encoding', () => {
beforeAll(async () => {
const config = await loadFixture('unicode-base')
const config = await loadFixture('encoding')
nuxt = new Nuxt(config)
await nuxt.ready()
@ -18,7 +18,7 @@ describe('unicode-base', () => {
test('/ö/ (router base)', async () => {
const { body: response } = await rp(url('/ö/'))
expect(response).toContain('<h1>Unicode base works!</h1>')
expect(response).toContain('Unicode base works!')
})
// Close server and ask nuxt to stop listening to file changes

View File

@ -1,26 +0,0 @@
import { resolve } from 'path'
import { getResourcesSize } from '../utils'
const distDir = resolve(__dirname, '../fixtures/unicode-base/.nuxt/dist')
describe('nuxt minimal vue-app bundle size limit', () => {
expect.extend({
toBeWithinSize (received, size) {
const maxSize = size * 1.02
const minSize = size * 0.98
const pass = received >= minSize && received <= maxSize
return {
pass,
message: () =>
`expected ${received} to be within range ${minSize} - ${maxSize}`
}
}
})
it('should stay within the size limit range', async () => {
const filter = filename => filename === 'vue-app.nuxt.js'
const legacyResourcesSize = await getResourcesSize(distDir, 'client', { filter })
const LEGACY_JS_RESOURCES_KB_SIZE = 17.1
expect(legacyResourcesSize.uncompressed).toBeWithinSize(LEGACY_JS_RESOURCES_KB_SIZE)
})
})

View File

@ -1,3 +1,3 @@
import { buildFixture } from '../../utils/build'
buildFixture('unicode-base')
buildFixture('encoding')

View File

@ -0,0 +1,32 @@
<template>
<div>
<div>
<NLink to="/тест">
/тест
</NLink>
<NLink :to="encodeURI('/тест')">
/тест (encoded)
</NLink>
<br>
<NLink to="/тест?spa">
/тест (SPA)
</NLink>
<NLink :to="encodeURI('/тест?spa')">
/тест (SPA encoded)
</NLink>
</div>
<Nuxt />
</div>
</template>
<style scoped>
a {
color: grey;
text-decoration: none;
}
.nuxt-link-exact-active {
color: red;
font-weight: bold;
}
</style>

12
test/fixtures/encoding/nuxt.config.js vendored Normal file
View File

@ -0,0 +1,12 @@
export default {
router: {
base: '/ö/'
},
hooks: {
'vue-renderer:context' (ssrContext) {
if (ssrContext.url.includes('?spa')) {
ssrContext.spa = true
}
}
}
}

View File

@ -0,0 +1,5 @@
<template>
<div>
Unicode base works!
</div>
</template>

View File

@ -1,43 +0,0 @@
export default {
modern: 'server',
router: {
base: '/%C3%B6/'
},
loading: false,
loadingIndicator: false,
fetch: {
client: false,
server: false
},
features: {
store: false,
layouts: false,
meta: false,
middleware: false,
transitions: false,
deprecations: false,
validate: false,
asyncData: false,
fetch: false,
clientOnline: false,
clientPrefetch: false,
clientUseUrl: true,
componentAliases: false,
componentClientOnly: false
},
build: {
indicator: false,
terser: true,
optimization: {
splitChunks: {
cacheGroups: {
nuxtApp: {
test: /[\\/]\.nuxt[\\/]/,
filename: 'vue-app.nuxt.js',
enforce: true
}
}
}
}
}
}

View File

@ -13403,6 +13403,11 @@ vue-router@3.4.8:
resolved "https://registry.npmjs.org/vue-router/-/vue-router-3.4.8.tgz#2c06261d35d8075893470352d42d70b6287b8194"
integrity sha512-3BsR84AqarcmweXjItxw3jwQsiYNssYg090yi4rlzTnCJxmHtkyCvhNz9Z7qRSOkmiV485KkUCReTp5AjNY4wg==
vue-router@^3.4.9:
version "3.4.9"
resolved "https://registry.npmjs.org/vue-router/-/vue-router-3.4.9.tgz#c016f42030ae2932f14e4748b39a1d9a0e250e66"
integrity sha512-CGAKWN44RqXW06oC+u4mPgHLQQi2t6vLD/JbGRDAXm0YpMv0bgpKuU5bBd7AvMgfTz9kXVRIWKHqRwGEb8xFkA==
vue-server-renderer@^2.6.12:
version "2.6.12"
resolved "https://registry.npmjs.org/vue-server-renderer/-/vue-server-renderer-2.6.12.tgz#a8cb9c49439ef205293cb41c35d0d2b0541653a5"