From 57ea6791f4179f177e8af238a500c5188257c99c Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Wed, 17 Dec 2025 09:29:30 +0000 Subject: [PATCH] fix(@angular/ssr): propagate status code to redirect This commit ensures that status codes set in code are propagated to the redirect when using `Router.navigate`. --- packages/angular/ssr/src/app.ts | 20 +-------- packages/angular/ssr/src/routes/ng-routes.ts | 8 +--- packages/angular/ssr/src/utils/redirect.ts | 46 ++++++++++++++++++++ packages/angular/ssr/test/app_spec.ts | 10 ++++- 4 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 packages/angular/ssr/src/utils/redirect.ts diff --git a/packages/angular/ssr/src/app.ts b/packages/angular/ssr/src/app.ts index 83b18a25ef72..4c6cbfd3cbf5 100644 --- a/packages/angular/ssr/src/app.ts +++ b/packages/angular/ssr/src/app.ts @@ -25,6 +25,7 @@ import { InlineCriticalCssProcessor } from './utils/inline-critical-css'; import { LRUCache } from './utils/lru-cache'; import { AngularBootstrap, renderAngular } from './utils/ng'; import { promiseWithAbort } from './utils/promise'; +import { createRedirectResponse } from './utils/redirect'; import { buildPathWithParams, joinUrlParts, stripLeadingSlash } from './utils/url'; /** @@ -351,7 +352,7 @@ export class AngularServerApp { } if (result.redirectTo) { - return createRedirectResponse(result.redirectTo, status); + return createRedirectResponse(result.redirectTo, responseInit.status); } if (renderMode === RenderMode.Prerender) { @@ -546,20 +547,3 @@ function appendPreloadHintsToHtml(html: string, preload: readonly string[]): str html.slice(bodyCloseIdx), ].join('\n'); } - -/** - * Creates an HTTP redirect response with a specified location and status code. - * - * @param location - The URL to which the response should redirect. - * @param status - The HTTP status code for the redirection. Defaults to 302 (Found). - * See: https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect_static#status - * @returns A `Response` object representing the HTTP redirect. - */ -function createRedirectResponse(location: string, status = 302): Response { - return new Response(null, { - status, - headers: { - 'Location': location, - }, - }); -} diff --git a/packages/angular/ssr/src/routes/ng-routes.ts b/packages/angular/ssr/src/routes/ng-routes.ts index e46dd685511a..b60e704371a4 100644 --- a/packages/angular/ssr/src/routes/ng-routes.ts +++ b/packages/angular/ssr/src/routes/ng-routes.ts @@ -28,6 +28,7 @@ import { Console } from '../console'; import { AngularAppManifest, getAngularAppManifest } from '../manifest'; import { AngularBootstrap, isNgModule } from '../utils/ng'; import { promiseWithAbort } from '../utils/promise'; +import { VALID_REDIRECT_RESPONSE_CODES, isValidRedirectResponseCode } from '../utils/redirect'; import { addTrailingSlash, joinUrlParts, stripLeadingSlash } from '../utils/url'; import { PrerenderFallback, @@ -59,11 +60,6 @@ const CATCH_ALL_REGEXP = /\/(\*\*)$/; */ const URL_PARAMETER_REGEXP = /(? { }) class RedirectComponent { constructor() { + const responseInit = inject(RESPONSE_INIT); + if (responseInit) { + // TODO(alanagius): Remove once https://github.com/angular/angular/pull/66126 is merged and released. + (responseInit as { status: number }).status = 308; + } + void inject(Router).navigate([], { queryParams: { filter: 'test' }, }); @@ -310,7 +316,7 @@ describe('AngularServerApp', () => { it('returns a 302 status and redirects to the correct location when `router.navigate` is used', async () => { const response = await app.handle(new Request('http://localhost/redirect-via-navigate')); expect(response?.headers.get('location')).toBe('/redirect-via-navigate?filter=test'); - expect(response?.status).toBe(302); + expect(response?.status).toBe(308); }); it('returns a 302 status and redirects to the correct location when `urlTree` is updated in a guard', async () => {