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

[sitecore-jss-nextjs] fix for Redirects related to x-middleware-next Header #SXA-6564 #1899

Merged
merged 4 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ Our versioning strategy is as follows:

### 🧹 Chores

## 22.1.2

### 🐛 Bug Fixes

* `[sitecore-jss-nextjs]` Resolved an issue with redirects that was caused by the x-middleware-next header in Next.js. This header prevented the flow from being interrupted properly, resulting in redirects not functioning correctly in certain cases. ([#1899](https://github.com/Sitecore/jss/pull/1899))

## 22.1.1

### 🎉 New Features & Improvements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1430,6 +1430,62 @@ describe('RedirectsMiddleware', () => {
});
});

it('should remove x-middleware-next header and redirect 301', async () => {
const siteName = 'foo';
const res = NextResponse.redirect('http://localhost:3000/found', {});
res.headers.set('x-middleware-next', '1');
res.cookies.set('sc_site', siteName);
const req = createRequest({
nextUrl: {
href: 'http://localhost:3000/not-found',
pathname: '/not-found',
locale: 'en',
search: '',
origin: 'http://localhost:3000',
clone() {
return Object.assign({}, req.nextUrl);
},
},
});

const { middleware, fetchRedirects, siteResolver } = createMiddleware({
pattern: 'not-found',
target: '/found',
redirectType: REDIRECT_TYPE_301,
isQueryStringPreserved: true,
locale: 'en',
});

const expected = NextResponse.redirect('http://localhost:3000/found', {
...res,
status: 301,
headers: {},
});

const finalRes = await middleware.getHandler()(req, res);

validateDebugLog('redirects middleware start: %o', {
hostname: 'foo.net',
language: 'en',
pathname: '/not-found',
});

validateEndMessageDebugLog('redirects middleware end in %dms: %o', {
headers: {
location: 'http://localhost:3000/found',
'set-cookie': 'sc_site=foo; Path=/',
},
redirected: false,
status: 301,
url: '',
});

expect(siteResolver.getByHost).not.called.to.equal(true);
expect(siteResolver.getByName).to.be.calledWith(siteName);
expect(fetchRedirects).to.be.calledWith(siteName);
expect(finalRes.status).to.equal(expected.status);
});

describe('should redirect to normalized path when nextjs specific "path" query string parameter is provided', () => {
it('should return 301 redirect', async () => {
const setCookies = () => {};
Expand Down
54 changes: 36 additions & 18 deletions packages/sitecore-jss-nextjs/src/middleware/redirects-middleware.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import regexParser from 'regex-parser';
import { NextResponse, NextRequest } from 'next/server';
import { debug } from '@sitecore-jss/sitecore-jss';
import {
RedirectInfo,
GraphQLRedirectsService,
GraphQLRedirectsServiceConfig,
REDIRECT_TYPE_301,
REDIRECT_TYPE_302,
REDIRECT_TYPE_SERVER_TRANSFER,
RedirectInfo,
SiteInfo,
} from '@sitecore-jss/sitecore-jss/site';
import { debug } from '@sitecore-jss/sitecore-jss';
import { NextRequest, NextResponse } from 'next/server';
import regexParser from 'regex-parser';
import { MiddlewareBase, MiddlewareBaseConfig } from './middleware';

const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
Expand Down Expand Up @@ -147,20 +147,12 @@ export class RedirectsMiddleware extends MiddlewareBase {

/** return Response redirect with http code of redirect type **/
switch (existsRedirect.redirectType) {
case REDIRECT_TYPE_301:
return NextResponse.redirect(redirectUrl, {
...res,
status: 301,
statusText: 'Moved Permanently',
headers: res?.headers,
});
case REDIRECT_TYPE_302:
return NextResponse.redirect(redirectUrl, {
...res,
status: 302,
statusText: 'Found',
headers: res?.headers,
});
case REDIRECT_TYPE_301: {
return this.createRedirectResponse(redirectUrl, res, 301, 'Moved Permanently');
}
case REDIRECT_TYPE_302: {
return this.createRedirectResponse(redirectUrl, res, 302, 'Found');
}
case REDIRECT_TYPE_SERVER_TRANSFER: {
return this.rewrite(redirectUrl, req, res || NextResponse.next());
}
Expand Down Expand Up @@ -267,4 +259,30 @@ export class RedirectsMiddleware extends MiddlewareBase {

return new URL(`${url.pathname}`, url.origin);
}

/**
* Helper function to create a redirect response and remove the x-middleware-next header.
* @param {string} url The URL to redirect to.
* @param {Response} res The response object.
* @param {number} status The HTTP status code of the redirect.
* @param {string} statusText The status text of the redirect.
* @returns {NextResponse<unknown>} The redirect response.
*/
private createRedirectResponse(
url: string,
res: Response | undefined,
status: number,
statusText: string
): NextResponse {
const redirect = NextResponse.redirect(url, {
...(res || {}),
status,
statusText,
headers: res?.headers,
});
if (res?.headers) {
redirect.headers.delete('x-middleware-next');
}
return redirect;
}
}