From: Eduardo San Martin Morote Date: Fri, 16 Aug 2019 17:16:57 +0000 (+0200) Subject: feat: encode query params X-Git-Tag: v4.0.0-alpha.0~265 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b254cbee20598ef296c820e0db377bdf0c8d5df1;p=thirdparty%2Fvuejs%2Frouter.git feat: encode query params --- diff --git a/__tests__/url-encoding.spec.js b/__tests__/url-encoding.spec.js index d201b699..e93c334b 100644 --- a/__tests__/url-encoding.spec.js +++ b/__tests__/url-encoding.spec.js @@ -6,7 +6,8 @@ const { createDom, components, tick, HistoryMock } = require('./utils') /** @type {import('../src/types').RouteRecord[]} */ const routes = [ - { path: '/%25', name: 'home', component: components.Home }, + { path: '/', name: 'home', component: components.Home }, + { path: '/%25', name: 'percent', component: components.Home }, { path: '/to-p/:p', redirect: to => `/p/${to.params.p}` }, { path: '/p/:p', component: components.Bar }, ] @@ -27,7 +28,7 @@ describe('URL Encoding', () => { await router.doInitialNavigation() expect(router.currentRoute).toEqual( expect.objectContaining({ - name: 'home', + name: 'percent', fullPath: '/%25', path: '/%25', }) @@ -83,5 +84,40 @@ describe('URL Encoding', () => { }) ) }) + + it('decodes params in query', async () => { + const history = createHistory('/?q=%25%E2%82%AC') + const router = new Router({ history, routes }) + await router.doInitialNavigation() + expect(router.currentRoute).toEqual( + expect.objectContaining({ + name: 'home', + fullPath: '/?q=' + encodeURIComponent('%€'), + query: { + q: '%€', + }, + path: '/', + }) + ) + }) + + it('allow unencoded params in query (IE Edge)', async () => { + const spy = jest.spyOn(console, 'warn').mockImplementation(() => {}) + const history = createHistory('/?q=€%notvalid') + const router = new Router({ history, routes }) + await router.doInitialNavigation() + expect(spy).toHaveBeenCalledTimes(1) + spy.mockRestore() + expect(router.currentRoute).toEqual( + expect.objectContaining({ + name: 'home', + fullPath: '/?q=' + encodeURIComponent('€%notvalid'), + query: { + q: '€%notvalid', + }, + path: '/', + }) + ) + }) }) }) diff --git a/src/history/base.ts b/src/history/base.ts index f844f322..5c878fa9 100644 --- a/src/history/base.ts +++ b/src/history/base.ts @@ -2,6 +2,8 @@ import * as utils from './utils' import { ListenerRemover } from '../types' export type HistoryQuery = Record +// TODO: is it reall worth allowing null to form queries like ?q&b&c +// When parsing using URLSearchParams, `q&c=` yield an empty string for q and c export type RawHistoryQuery = Record export interface HistoryLocation { diff --git a/src/history/utils.ts b/src/history/utils.ts index 3ad84263..cde856d7 100644 --- a/src/history/utils.ts +++ b/src/history/utils.ts @@ -28,7 +28,7 @@ export function parseURL(location: string): HistoryLocationNormalized { hashPos > -1 ? hashPos : location.length ) - query = parseQuery(searchString) + query = normalizeQuery(parseQuery(searchString)) } if (hashPos > -1) { @@ -59,7 +59,15 @@ export function parseQuery(search: string): HistoryQuery { if (search === '' || search === '?') return query const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&') for (let i = 0; i < searchParams.length; ++i) { - const [key, value] = searchParams[i].split('=') + let [key, value] = searchParams[i].split('=') + try { + value = decodeURIComponent(value) + } catch (err) { + // TODO: handling only URIError? + console.warn( + `[vue-router] error decoding "${value}" while parsing query. Sticking to the original value.` + ) + } if (key in query) { // an extra variable for ts types let currentValue = query[key] @@ -94,15 +102,26 @@ export function stringifyQuery(query: RawHistoryQuery): string { // TODO: util function? for (const key in query) { if (search.length > 1) search += '&' - // TODO: handle array const value = query[key] - if (Array.isArray(value)) { - search += `${key}=${value[0]}` - for (let i = 1; i < value.length; i++) { - search += `&${key}=${value[i]}` - } - } else { - search += `${key}=${query[key]}` + if (value === null) { + // TODO: should we just add the empty string value? + search += key + continue + } + + let values: string[] = Array.isArray(value) ? value : [value] + try { + values = values.map(encodeURIComponent) + } catch (err) { + // TODO: handling only URIError? + + console.warn( + `[vue-router] invalid query parameter while stringifying query: "${key}": "${values}"` + ) + } + search += `${key}=${values[0]}` + for (let i = 1; i < values.length; i++) { + search += `&${key}=${values[i]}` } } @@ -111,7 +130,13 @@ export function stringifyQuery(query: RawHistoryQuery): string { export function normalizeQuery(query: RawHistoryQuery): HistoryQuery { // TODO: implem - return query as HistoryQuery + const normalizedQuery: HistoryQuery = {} + for (const key in query) { + const value = query[key] + if (value === null) normalizedQuery[key] = '' + else normalizedQuery[key] = value + } + return normalizedQuery } /**