From 5585e81af4ac22110efaec2d3a22d82091eba539 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 3 Feb 2022 18:11:25 -0600 Subject: [PATCH 1/5] Ensure non-matching API routes can be rewritten --- packages/next/server/base-server.ts | 8 --- packages/next/server/router.ts | 14 +++++ test/e2e/i18n-api-support/index.test.ts | 71 +++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 test/e2e/i18n-api-support/index.test.ts diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index 0d28b29ae6be..099b8c84bcc0 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -543,14 +543,6 @@ export default abstract class Server { addRequestMeta(req, '__nextIsLocaleDomain', true) } - if (url.locale?.path.detectedLocale) { - req.url = formatUrl(url) - addRequestMeta(req, '__nextStrippedLocale', true) - if (url.pathname === '/api' || url.pathname.startsWith('/api/')) { - return this.render404(req, res, parsedUrl) - } - } - if (!this.minimalMode || !parsedUrl.query.__nextLocale) { if (url?.locale?.locale) { parsedUrl.query.__nextLocale = url.locale.locale diff --git a/packages/next/server/router.ts b/packages/next/server/router.ts index 1c9fc3edfffb..e01d0913ef36 100644 --- a/packages/next/server/router.ts +++ b/packages/next/server/router.ts @@ -8,6 +8,7 @@ import { RouteHas } from '../lib/load-custom-routes' import { matchHas } from '../shared/lib/router/utils/prepare-destination' import { getRequestMeta } from './request-meta' import { BaseNextRequest, BaseNextResponse } from './base-http' +import { API_ROUTE } from '../lib/constants' export const route = pathMatch() @@ -303,6 +304,7 @@ export default class Router { const keepBasePath = isCustomRoute || isPublicFolderCatchall || isMiddlewareCatchall const keepLocale = isCustomRoute + const isPageChecker = testRoute.name.includes('page check') const currentPathnameNoBasePath = replaceBasePath( currentPathname, @@ -317,8 +319,20 @@ export default class Router { currentPathnameNoBasePath, this.locales ) + const activeBasePath = keepBasePath ? this.basePath : '' + // don't match API routes when they are locale prefixed + // e.g. /api/hello shouldn't match /en/api/hello as a page + // rewrites/redirects can match though + if ( + isPageChecker && + localePathResult.detectedLocale && + localePathResult.pathname.match(API_ROUTE) + ) { + continue + } + if (keepLocale) { if ( !testRoute.internal && diff --git a/test/e2e/i18n-api-support/index.test.ts b/test/e2e/i18n-api-support/index.test.ts new file mode 100644 index 000000000000..d1ffbfec948d --- /dev/null +++ b/test/e2e/i18n-api-support/index.test.ts @@ -0,0 +1,71 @@ +import { createNext } from 'e2e-utils' +import { fetchViaHTTP } from 'next-test-utils' +import { NextInstance } from 'test/lib/next-modes/base' + +describe('i18n API support', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + 'pages/api/hello.js': ` + export default function handler(req, res) { + res.end('hello world') + } + `, + 'pages/api/blog/[slug].js': ` + export default function handler(req, res) { + res.end('blog/[slug]') + } + `, + }, + nextConfig: { + i18n: { + locales: ['en', 'fr'], + defaultLocale: 'en', + }, + async rewrites() { + return { + beforeFiles: [], + afterFiles: [], + fallback: [ + { + source: '/api/:path*', + destination: 'https://example.com/', + }, + ], + } + }, + }, + dependencies: {}, + }) + }) + afterAll(() => next.destroy()) + + it('should respond to normal API request', async () => { + const res = await fetchViaHTTP(next.url, '/api/hello') + expect(res.status).toBe(200) + expect(await res.text()).toBe('hello world') + }) + + it('should respond to normal dynamic API request', async () => { + const res = await fetchViaHTTP(next.url, '/api/blog/first') + expect(res.status).toBe(200) + expect(await res.text()).toBe('blog/[slug]') + }) + + it('should fallback rewrite non-matching API request', async () => { + const paths = [ + '/fr/api/hello', + '/en/api/blog/first', + '/en/api/non-existent', + '/api/non-existent', + ] + + for (const path of paths) { + const res = await fetchViaHTTP(next.url, path) + expect(res.status).toBe(200) + expect(await res.text()).toContain('Example Domain') + } + }) +}) From e225f5e5bda2f298ab3d05fa3f62f4812e493750 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 3 Feb 2022 18:20:44 -0600 Subject: [PATCH 2/5] re-add snippet --- packages/next/server/base-server.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index 099b8c84bcc0..e8d407011eb8 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -543,6 +543,11 @@ export default abstract class Server { addRequestMeta(req, '__nextIsLocaleDomain', true) } + if (url.locale?.path.detectedLocale) { + req.url = formatUrl(url) + addRequestMeta(req, '__nextStrippedLocale', true) + } + if (!this.minimalMode || !parsedUrl.query.__nextLocale) { if (url?.locale?.locale) { parsedUrl.query.__nextLocale = url.locale.locale From 272432f61a843409eac6664791e35e8e52914a73 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 3 Feb 2022 18:26:09 -0600 Subject: [PATCH 3/5] Update check --- packages/next/server/router.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/server/router.ts b/packages/next/server/router.ts index e01d0913ef36..3c4c49d12662 100644 --- a/packages/next/server/router.ts +++ b/packages/next/server/router.ts @@ -326,7 +326,7 @@ export default class Router { // e.g. /api/hello shouldn't match /en/api/hello as a page // rewrites/redirects can match though if ( - isPageChecker && + !isCustomRoute && localePathResult.detectedLocale && localePathResult.pathname.match(API_ROUTE) ) { From 3805017501f3da580bd0c0a4b8fc418528b03a08 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 3 Feb 2022 20:59:48 -0600 Subject: [PATCH 4/5] remove middleware conflicting import --- packages/next/server/router.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/next/server/router.ts b/packages/next/server/router.ts index 3c4c49d12662..0e8af46e8aa5 100644 --- a/packages/next/server/router.ts +++ b/packages/next/server/router.ts @@ -8,7 +8,6 @@ import { RouteHas } from '../lib/load-custom-routes' import { matchHas } from '../shared/lib/router/utils/prepare-destination' import { getRequestMeta } from './request-meta' import { BaseNextRequest, BaseNextResponse } from './base-http' -import { API_ROUTE } from '../lib/constants' export const route = pathMatch() @@ -304,7 +303,6 @@ export default class Router { const keepBasePath = isCustomRoute || isPublicFolderCatchall || isMiddlewareCatchall const keepLocale = isCustomRoute - const isPageChecker = testRoute.name.includes('page check') const currentPathnameNoBasePath = replaceBasePath( currentPathname, @@ -328,7 +326,7 @@ export default class Router { if ( !isCustomRoute && localePathResult.detectedLocale && - localePathResult.pathname.match(API_ROUTE) + localePathResult.pathname.match(/^\/api(?:\/|$)/) ) { continue } From bdfe2cb3942ce6cc8666f76074183c6c46e4dd90 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 1 Mar 2022 12:02:19 -0800 Subject: [PATCH 5/5] update test domain --- test/e2e/i18n-api-support/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/i18n-api-support/index.test.ts b/test/e2e/i18n-api-support/index.test.ts index d1ffbfec948d..fa763d74d231 100644 --- a/test/e2e/i18n-api-support/index.test.ts +++ b/test/e2e/i18n-api-support/index.test.ts @@ -31,7 +31,7 @@ describe('i18n API support', () => { fallback: [ { source: '/api/:path*', - destination: 'https://example.com/', + destination: 'https://example.vercel.sh/', }, ], }