mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-26 23:52:06 +00:00
fix(server, vue-app): address encoding issues with query params (#8748)
Co-authored-by: Pooya Parsa <pyapar@gmail.com>
This commit is contained in:
parent
0d4974968e
commit
07dd2cce9b
@ -23,7 +23,8 @@
|
|||||||
"pify": "^5.0.0",
|
"pify": "^5.0.0",
|
||||||
"serve-placeholder": "^1.2.3",
|
"serve-placeholder": "^1.2.3",
|
||||||
"serve-static": "^1.14.1",
|
"serve-static": "^1.14.1",
|
||||||
"server-destroy": "^1.0.1"
|
"server-destroy": "^1.0.1",
|
||||||
|
"ufo": "^0.6.1"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import generateETag from 'etag'
|
import generateETag from 'etag'
|
||||||
import fresh from 'fresh'
|
import fresh from 'fresh'
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
|
import { normalizeURL } from 'ufo'
|
||||||
|
|
||||||
import { getContext, TARGETS } from '@nuxt/utils'
|
import { getContext, TARGETS } from '@nuxt/utils'
|
||||||
|
|
||||||
@ -9,7 +10,7 @@ export default ({ options, nuxt, renderRoute, resources }) => async function nux
|
|||||||
const context = getContext(req, res)
|
const context = getContext(req, res)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = decodeURI(req.url)
|
const url = normalizeURL(req.url)
|
||||||
res.statusCode = 200
|
res.statusCode = 200
|
||||||
const result = await renderRoute(url, context)
|
const result = await renderRoute(url, context)
|
||||||
|
|
||||||
|
@ -329,18 +329,24 @@ describe('server: nuxtMiddleware', () => {
|
|||||||
expect(consola.error).toBeCalledWith(err)
|
expect(consola.error).toBeCalledWith(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should return 400 if request is uri error', async () => {
|
test('should return handle uri errors by normalizing', async () => {
|
||||||
const context = createContext()
|
const context = createContext()
|
||||||
const result = { html: 'rendered html' }
|
const result = { html: 'rendered html' }
|
||||||
context.renderRoute.mockReturnValue(result)
|
context.renderRoute.mockReturnValue(result)
|
||||||
const nuxtMiddleware = createNuxtMiddleware(context)
|
const nuxtMiddleware = createNuxtMiddleware(context)
|
||||||
const { req, res, next } = createServerContext()
|
const { req, res, next } = createServerContext()
|
||||||
|
const paths = ['%c1%81', '%c1', '%']
|
||||||
|
|
||||||
const err = Error('URI malformed')
|
for (const path of paths) {
|
||||||
err.name = 'URIError'
|
await nuxtMiddleware(
|
||||||
|
{ ...req, url: 'http://localhost/test/server/' + path },
|
||||||
|
res,
|
||||||
|
next
|
||||||
|
)
|
||||||
|
|
||||||
await nuxtMiddleware({ ...req, url: 'http://localhost/test/server/%c1%81' }, res, next)
|
expect(next).toBeCalledTimes(0)
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
expect(next).toBeCalledWith(err)
|
next.mockReset()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -101,14 +101,6 @@ export const routerOptions = {
|
|||||||
fallback: <%= router.fallback %>
|
fallback: <%= router.fallback %>
|
||||||
}
|
}
|
||||||
|
|
||||||
function decodeObj(obj) {
|
|
||||||
for (const key in obj) {
|
|
||||||
if (typeof obj[key] === 'string') {
|
|
||||||
obj[key] = decode(obj[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createRouter (ssrContext, config) {
|
export function createRouter (ssrContext, config) {
|
||||||
const base = (config.app && config.app.basePath) || routerOptions.base
|
const base = (config.app && config.app.basePath) || routerOptions.base
|
||||||
const router = new Router({ ...routerOptions, base })
|
const router = new Router({ ...routerOptions, base })
|
||||||
@ -124,11 +116,7 @@ export function createRouter (ssrContext, config) {
|
|||||||
if (typeof to === 'string') {
|
if (typeof to === 'string') {
|
||||||
to = normalizeURL(to)
|
to = normalizeURL(to)
|
||||||
}
|
}
|
||||||
const r = resolve(to, current, append)
|
return resolve(to, current, append)
|
||||||
if (r && r.resolved && r.resolved.query) {
|
|
||||||
decodeObj(r.resolved.query)
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return router
|
return router
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import fetch from 'node-fetch'
|
||||||
import { getPort, loadFixture, Nuxt, rp } from '../utils'
|
import { getPort, loadFixture, Nuxt, rp } from '../utils'
|
||||||
|
|
||||||
let port
|
let port
|
||||||
@ -22,7 +23,7 @@ describe('encoding', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('/ö/dynamic?q=food,coffee (encodeURIComponent)', async () => {
|
test('/ö/dynamic?q=food,coffee (encodeURIComponent)', async () => {
|
||||||
const { body: response } = await rp(url('/ö/dynamic?q=food%252Ccoffee'))
|
const { body: response } = await rp(url('/ö/dynamic?q=food,coffee'))
|
||||||
|
|
||||||
expect(response).toContain('food,coffee')
|
expect(response).toContain('food,coffee')
|
||||||
})
|
})
|
||||||
@ -33,6 +34,41 @@ describe('encoding', () => {
|
|||||||
expect(response).toContain('About')
|
expect(response).toContain('About')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('query params', async () => {
|
||||||
|
const queryStrings = {
|
||||||
|
'?email=some%20email.com': { email: 'some email.com' },
|
||||||
|
'?str=%26&str2=%2526': { str: '&', str2: '%26' },
|
||||||
|
'?t=coffee%2Cfood%2C': { t: 'coffee,food,' },
|
||||||
|
'?redirect=%2Fhomologation%2Flist': { redirect: '/homologation/list' },
|
||||||
|
'?email=some@email.com&token=DvtiwbIzry319e6KWimopA%3D%3D': {
|
||||||
|
email: 'some@email.com',
|
||||||
|
token: 'DvtiwbIzry319e6KWimopA=='
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const [param, result] of Object.entries(queryStrings)) {
|
||||||
|
const { body: response } = await rp(url('/ö/dynamic/test') + param)
|
||||||
|
expect(response).toContain(
|
||||||
|
JSON.stringify(result)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('invalidly encoded route params are handled', async () => {
|
||||||
|
const paths = ['%c1%81', '%c1', '%']
|
||||||
|
for (const path of paths) {
|
||||||
|
// We use node-fetch because got uses decodeURI on url and throws its own error
|
||||||
|
const response = await fetch(url('/ö/dynamic/') + path)
|
||||||
|
expect(response.ok).toBeTruthy()
|
||||||
|
expect(await response.text()).toContain(
|
||||||
|
JSON.stringify({ id: path })
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Close server and ask nuxt to stop listening to file changes
|
// Close server and ask nuxt to stop listening to file changes
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await nuxt.close()
|
await nuxt.close()
|
||||||
|
2
test/fixtures/encoding/layouts/default.vue
vendored
2
test/fixtures/encoding/layouts/default.vue
vendored
@ -26,6 +26,8 @@ export default {
|
|||||||
'/@about',
|
'/@about',
|
||||||
'/тест',
|
'/тест',
|
||||||
encodeURI('/тест'),
|
encodeURI('/тест'),
|
||||||
|
'/dynamic/%c',
|
||||||
|
'/dynamic/%',
|
||||||
'/dynamic/سلام چطوری?q=cofee,food,دسر',
|
'/dynamic/سلام چطوری?q=cofee,food,دسر',
|
||||||
encodeURI('/dynamic/سلام چطوری?q=cofee,food,دسر'),
|
encodeURI('/dynamic/سلام چطوری?q=cofee,food,دسر'),
|
||||||
// Using encodeURIComponent on each segment
|
// Using encodeURIComponent on each segment
|
||||||
|
Loading…
Reference in New Issue
Block a user