diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 8aa3adc1e1c5a..c7ccd7113208d 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -369,10 +369,9 @@ export default class Server { // interpolate dynamic params and normalize URL if needed if (pageIsDynamic) { let params: ParsedUrlQuery | false = {} - const paramsResult = utils.normalizeDynamicRouteParams({ - ...parsedUrl.query, - ...query, - }) + + Object.assign(parsedUrl.query, query) + const paramsResult = utils.normalizeDynamicRouteParams(parsedUrl.query) if (paramsResult.hasValidParams) { params = paramsResult.params @@ -407,6 +406,7 @@ export default class Server { pathname: matchedPathname, }) } + Object.assign(parsedUrl.query, params) utils.normalizeVercelUrl(req, true) } diff --git a/test/integration/required-server-files/pages/api/optional/[[...rest]].js b/test/integration/required-server-files/pages/api/optional/[[...rest]].js new file mode 100644 index 0000000000000..a5395a602d14c --- /dev/null +++ b/test/integration/required-server-files/pages/api/optional/[[...rest]].js @@ -0,0 +1,7 @@ +export default (req, res) => { + console.log(req.url, 'query', req.query) + res.json({ + url: req.url, + query: req.query, + }) +} diff --git a/test/integration/required-server-files/pages/optional-ssg/[[...rest]].js b/test/integration/required-server-files/pages/optional-ssg/[[...rest]].js new file mode 100644 index 0000000000000..dbe91169f2a77 --- /dev/null +++ b/test/integration/required-server-files/pages/optional-ssg/[[...rest]].js @@ -0,0 +1,20 @@ +export const getStaticProps = ({ params }) => { + return { + props: { + random: Math.random(), + params: params || null, + }, + revalidate: 1, + } +} + +export const getStaticPaths = () => { + return { + paths: [], + fallback: true, + } +} + +export default function Page(props) { + return

{JSON.stringify(props)}

+} diff --git a/test/integration/required-server-files/pages/optional-ssp/[[...rest]].js b/test/integration/required-server-files/pages/optional-ssp/[[...rest]].js new file mode 100644 index 0000000000000..53a840b79e21d --- /dev/null +++ b/test/integration/required-server-files/pages/optional-ssp/[[...rest]].js @@ -0,0 +1,13 @@ +export const getServerSideProps = ({ query, params }) => { + return { + props: { + random: Math.random(), + query: query, + params: params || null, + }, + } +} + +export default function Page(props) { + return

{JSON.stringify(props)}

+} diff --git a/test/integration/required-server-files/test/index.test.js b/test/integration/required-server-files/test/index.test.js index f5a0cafd9dfd9..399fcfedbc748 100644 --- a/test/integration/required-server-files/test/index.test.js +++ b/test/integration/required-server-files/test/index.test.js @@ -448,4 +448,58 @@ describe('Required Server Files', () => { expect(errors.length).toBe(1) expect(errors[0].message).toContain('gsp hit an oops') }) + + it('should normalize optional values correctly for SSP page', async () => { + const res = await fetchViaHTTP( + appPort, + '/optional-ssp', + { rest: '', another: 'value' }, + { + headers: { + 'x-matched-path': '/optional-ssp/[[...rest]]', + }, + } + ) + + const html = await res.text() + const $ = cheerio.load(html) + const props = JSON.parse($('#props').text()) + expect(props.params).toEqual({}) + expect(props.query).toEqual({ another: 'value' }) + }) + + it('should normalize optional values correctly for SSG page', async () => { + const res = await fetchViaHTTP( + appPort, + '/optional-ssg', + { rest: '', another: 'value' }, + { + headers: { + 'x-matched-path': '/optional-ssg/[[...rest]]', + }, + } + ) + + const html = await res.text() + const $ = cheerio.load(html) + const props = JSON.parse($('#props').text()) + expect(props.params).toEqual({}) + }) + + it('should normalize optional values correctly for API page', async () => { + const res = await fetchViaHTTP( + appPort, + '/api/optional', + { rest: '', another: 'value' }, + { + headers: { + 'x-matched-path': '/api/optional/[[...rest]]', + }, + } + ) + + const json = await res.json() + expect(json.query).toEqual({ another: 'value' }) + expect(json.url).toBe('/api/optional?another=value') + }) })