Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug fix: dynamic page should not be interpreted as predefined page #33808

Merged
21 changes: 13 additions & 8 deletions packages/next/server/base-server.ts
Expand Up @@ -1532,15 +1532,20 @@ export default abstract class Server {
delete query._nextBubbleNoFallback

try {
const result = await this.findPageComponents(pathname, query)
if (result) {
try {
return await this.renderToResponseWithComponents(ctx, result)
} catch (err) {
const isNoFallbackError = err instanceof NoFallbackError
// The following guard is necessary to safely cast the URL pathname to a potential predefined page path.
// Without the guard, a request to the URL /accounts/[id] will find pages/accounts/[id].js
// and interpret it as a predefined route.
jameshfisher marked this conversation as resolved.
Show resolved Hide resolved
if (!isDynamicRoute(pathname)) {
const result = await this.findPageComponents(pathname, query)
if (result) {
try {
return await this.renderToResponseWithComponents(ctx, result)
} catch (err) {
const isNoFallbackError = err instanceof NoFallbackError

if (!isNoFallbackError || (isNoFallbackError && bubbleNoFallback)) {
throw err
if (!isNoFallbackError || (isNoFallbackError && bubbleNoFallback)) {
throw err
}
}
}
}
Expand Down
65 changes: 65 additions & 0 deletions test/e2e/dynamic-route-interpolation/index.test.ts
@@ -0,0 +1,65 @@
import { createNext } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { renderViaHTTP } from 'next-test-utils'
import cheerio from 'cheerio'

describe('Dynamic Route Interpolation', () => {
let next: NextInstance

beforeAll(async () => {
next = await createNext({
files: {
'pages/blog/[slug].js': `
export function getServerSideProps({ params }) {
return { props: { slug: params.slug } }
}

export default function Page(props) {
return <p id="slug">{props.slug}</p>
}

`,

'pages/api/dynamic/[slug].js': `
export default function Page(req, res) {
const { slug } = req.query
res.end('slug: ' + slug)
}

`,
},
dependencies: {},
})
})
afterAll(() => next.destroy())

it('should work', async () => {
const html = await renderViaHTTP(next.url, '/blog/a')
const $ = cheerio.load(html)
expect($('#slug').text()).toBe('a')
})

it('should work with parameter itself', async () => {
const html = await renderViaHTTP(next.url, '/blog/[slug]')
const $ = cheerio.load(html)
expect($('#slug').text()).toBe('[slug]')
})

it('should work with brackets', async () => {
const html = await renderViaHTTP(next.url, '/blog/[abc]')
const $ = cheerio.load(html)
expect($('#slug').text()).toBe('[abc]')
})

it('should work with parameter itself in API routes', async () => {
const html = await renderViaHTTP(next.url, '/api/dynamic/[slug]')
const $ = cheerio.load(html)
expect($('#slug').text()).toBe('[slug]')
})

it('should work with brackets in API routes', async () => {
const html = await renderViaHTTP(next.url, '/api/dynamic/[abc]')
const $ = cheerio.load(html)
expect($('#slug').text()).toBe('[abc]')
})
})