diff --git a/packages/next/server/web/spec-extension/response.ts b/packages/next/server/web/spec-extension/response.ts index 862f0935164cd..543b89de64ea3 100644 --- a/packages/next/server/web/spec-extension/response.ts +++ b/packages/next/server/web/spec-extension/response.ts @@ -37,29 +37,34 @@ export class NextResponse extends Response { return new NextResponse(response.body, response) } - static redirect(url: string | NextURL | URL, status = 307) { + static redirect(url: string | NextURL | URL, init?: number | ResponseInit) { + const status = typeof init === 'number' ? init : init?.status ?? 307 if (!REDIRECTS.has(status)) { throw new RangeError( 'Failed to execute "redirect" on "response": Invalid status code' ) } + const initObj = typeof init === 'object' ? init : {} + const headers = new Headers(initObj?.headers) + headers.set('Location', validateURL(url)) return new NextResponse(null, { - headers: { Location: validateURL(url) }, + ...initObj, + headers, status, }) } - static rewrite(destination: string | NextURL | URL) { - return new NextResponse(null, { - headers: { 'x-middleware-rewrite': validateURL(destination) }, - }) + static rewrite(destination: string | NextURL | URL, init?: ResponseInit) { + const headers = new Headers(init?.headers) + headers.set('x-middleware-rewrite', validateURL(destination)) + return new NextResponse(null, { ...init, headers }) } - static next() { - return new NextResponse(null, { - headers: { 'x-middleware-next': '1' }, - }) + static next(init?: ResponseInit) { + const headers = new Headers(init?.headers) + headers.set('x-middleware-next', '1') + return new NextResponse(null, { ...init, headers }) } } diff --git a/test/e2e/middleware-general/app/middleware.js b/test/e2e/middleware-general/app/middleware.js index f93c88cd1ed05..058041e03414b 100644 --- a/test/e2e/middleware-general/app/middleware.js +++ b/test/e2e/middleware-general/app/middleware.js @@ -37,9 +37,9 @@ export async function middleware(request) { const url = request.nextUrl if (request.headers.get('x-prerender-revalidate')) { - const res = NextResponse.next() - res.headers.set('x-middleware', 'hi') - return res + return NextResponse.next({ + headers: { 'x-middleware': 'hi' }, + }) } // this is needed for tests to get the BUILD_ID @@ -187,6 +187,15 @@ export async function middleware(request) { return NextResponse.rewrite(new URL('/about/a', request.url)) } + if (url.pathname === '/redirect-to-somewhere') { + url.pathname = '/somewhere' + return NextResponse.redirect(url, { + headers: { + 'x-redirect-header': 'hi', + }, + }) + } + if (url.pathname.startsWith('/url')) { try { if (request.nextUrl.pathname === '/url/relative-url') { @@ -229,18 +238,18 @@ export async function middleware(request) { throw new Error('test error') } - const response = NextResponse.next() const original = new URL(request.url) - response.headers.set('req-url-path', `${original.pathname}${original.search}`) - response.headers.set('req-url-basepath', request.nextUrl.basePath) - response.headers.set('req-url-pathname', request.nextUrl.pathname) - response.headers.set('req-url-query', request.nextUrl.searchParams.get('foo')) - response.headers.set('req-url-locale', request.nextUrl.locale) - response.headers.set( - 'req-url-params', - url.pathname !== '/static' ? JSON.stringify(params(request.url)) : '{}' - ) - return response + return NextResponse.next({ + headers: { + 'req-url-path': `${original.pathname}${original.search}`, + 'req-url-basepath': request.nextUrl.basePath, + 'req-url-pathname': request.nextUrl.pathname, + 'req-url-query': request.nextUrl.searchParams.get('foo'), + 'req-url-locale': request.nextUrl.locale, + 'req-url-params': + url.pathname !== '/static' ? JSON.stringify(params(request.url)) : '{}', + }, + }) } function serializeData(data) { diff --git a/test/e2e/middleware-general/test/index.test.ts b/test/e2e/middleware-general/test/index.test.ts index 7e2d46cedba3f..92543d2b9fbb0 100644 --- a/test/e2e/middleware-general/test/index.test.ts +++ b/test/e2e/middleware-general/test/index.test.ts @@ -149,6 +149,22 @@ describe('Middleware Runtime', () => { }) } + it('should have init header for NextResponse.redirect', async () => { + const res = await fetchViaHTTP( + next.url, + '/redirect-to-somewhere', + undefined, + { + redirect: 'manual', + } + ) + expect(res.status).toBe(307) + expect(new URL(res.headers.get('location'), 'http://n').pathname).toBe( + '/somewhere' + ) + expect(res.headers.get('x-redirect-header')).toBe('hi') + }) + it('should have correct query values for rewrite to ssg page', async () => { const browser = await webdriver(next.url, '/to-ssg') await browser.eval('window.beforeNav = 1') diff --git a/test/e2e/middleware-rewrites/app/middleware.js b/test/e2e/middleware-rewrites/app/middleware.js index 241b52a023077..bef33bcad1882 100644 --- a/test/e2e/middleware-rewrites/app/middleware.js +++ b/test/e2e/middleware-rewrites/app/middleware.js @@ -54,7 +54,9 @@ export async function middleware(request) { if (url.pathname === '/rewrite-me-to-about') { url.pathname = '/about' - return NextResponse.rewrite(url) + return NextResponse.rewrite(url, { + headers: { 'x-rewrite-target': String(url) }, + }) } if (url.pathname === '/rewrite-me-with-a-colon') { @@ -90,9 +92,9 @@ export async function middleware(request) { ? '/about-bypass' : '/about' - const response = NextResponse.rewrite(url) - response.headers.set('x-middleware-cache', 'no-cache') - return response + return NextResponse.rewrite(url, { + headers: { 'x-middleware-cache': 'no-cache' }, + }) } if (url.pathname.endsWith('/dynamic-replace')) {