mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
fix(routing): correct redirects in dev (#12169)
* fix(routing): correct redirects in dev * simplify code * address feedback * move delay inside the function, to keep the comment in one place
This commit is contained in:
parent
1cd30852a3
commit
15fa9babf3
6 changed files with 68 additions and 12 deletions
7
.changeset/light-pears-accept.md
Normal file
7
.changeset/light-pears-accept.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes a bug where configured redirects were incorrectly constructed when reading the file system.
|
||||
|
||||
This caused an issue where configuring a redirect in `astro.config.mjs` like `{ /old: /new }`, failed to trigger the correct redirect in the dev server.
|
7
.changeset/three-days-cough.md
Normal file
7
.changeset/three-days-cough.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes a bug where the dev server was not providing a consistent user experience for configured redirects.
|
||||
|
||||
With the fix, when you configure a redirect in `astro.config.mjs` like this `{ /old: "/new" }`, the dev server return an HTML response that matches the one emitted by a static build.
|
|
@ -33,6 +33,7 @@ import { getRedirectLocationOrThrow, routeIsRedirect } from '../redirects/index.
|
|||
import { RenderContext } from '../render-context.js';
|
||||
import { callGetStaticPaths } from '../render/route-cache.js';
|
||||
import { createRequest } from '../request.js';
|
||||
import { redirectTemplate } from '../routing/3xx.js';
|
||||
import { matchRoute } from '../routing/match.js';
|
||||
import { stringifyParams } from '../routing/params.js';
|
||||
import { getOutputFilename } from '../util.js';
|
||||
|
@ -445,17 +446,7 @@ async function generatePath(
|
|||
const siteURL = config.site;
|
||||
const location = siteURL ? new URL(locationSite, siteURL) : locationSite;
|
||||
const fromPath = new URL(request.url).pathname;
|
||||
// A short delay causes Google to interpret the redirect as temporary.
|
||||
// https://developers.google.com/search/docs/crawling-indexing/301-redirects#metarefresh
|
||||
const delay = response.status === 302 ? 2 : 0;
|
||||
body = `<!doctype html>
|
||||
<title>Redirecting to: ${location}</title>
|
||||
<meta http-equiv="refresh" content="${delay};url=${location}">
|
||||
<meta name="robots" content="noindex">
|
||||
<link rel="canonical" href="${location}">
|
||||
<body>
|
||||
<a href="${location}">Redirecting from <code>${fromPath}</code> to <code>${location}</code></a>
|
||||
</body>`;
|
||||
body = redirectTemplate({ status: response.status, location, from: fromPath });
|
||||
if (config.compressHTML === true) {
|
||||
body = body.replaceAll('\n', '');
|
||||
}
|
||||
|
|
19
packages/astro/src/core/routing/3xx.ts
Normal file
19
packages/astro/src/core/routing/3xx.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
export type RedirectTemplate = {
|
||||
from: string;
|
||||
location: string | URL;
|
||||
status: number;
|
||||
};
|
||||
|
||||
export function redirectTemplate({ status, location, from }: RedirectTemplate) {
|
||||
// A short delay causes Google to interpret the redirect as temporary.
|
||||
// https://developers.google.com/search/docs/crawling-indexing/301-redirects#metarefresh
|
||||
const delay = status === 302 ? 2 : 0;
|
||||
return `<!doctype html>
|
||||
<title>Redirecting to: ${location}</title>
|
||||
<meta http-equiv="refresh" content="${delay};url=${location}">
|
||||
<meta name="robots" content="noindex">
|
||||
<link rel="canonical" href="${location}">
|
||||
<body>
|
||||
<a href="${location}">Redirecting from <code>${from}</code> to <code>${location}</code></a>
|
||||
</body>`;
|
||||
}
|
|
@ -498,8 +498,14 @@ export async function createRouteManifest(
|
|||
|
||||
const redirectRoutes = createRedirectRoutes(params, routeMap, logger);
|
||||
|
||||
// we remove the file based routes that were deemed redirects
|
||||
const filteredFiledBasedRoutes = fileBasedRoutes.filter((fileBasedRoute) => {
|
||||
const isRedirect = redirectRoutes.findIndex((rd) => rd.route === fileBasedRoute.route);
|
||||
return isRedirect < 0;
|
||||
});
|
||||
|
||||
const routes: RouteData[] = [
|
||||
...[...fileBasedRoutes, ...injectedRoutes, ...redirectRoutes].sort(routeComparator),
|
||||
...[...filteredFiledBasedRoutes, ...injectedRoutes, ...redirectRoutes].sort(routeComparator),
|
||||
];
|
||||
|
||||
settings.buildOutput = getPrerenderDefault(config) ? 'static' : 'server';
|
||||
|
|
|
@ -8,9 +8,11 @@ import {
|
|||
import { AstroErrorData, isAstroError } from '../core/errors/index.js';
|
||||
import { req } from '../core/messages.js';
|
||||
import { loadMiddleware } from '../core/middleware/loadMiddleware.js';
|
||||
import { routeIsRedirect } from '../core/redirects/index.js';
|
||||
import { RenderContext } from '../core/render-context.js';
|
||||
import { type SSROptions, getProps } from '../core/render/index.js';
|
||||
import { createRequest } from '../core/request.js';
|
||||
import { redirectTemplate } from '../core/routing/3xx.js';
|
||||
import { matchAllRoutes } from '../core/routing/index.js';
|
||||
import { getSortedPreloadedMatches } from '../prerender/routing.js';
|
||||
import type { ComponentInstance, ManifestData } from '../types/astro.js';
|
||||
|
@ -286,6 +288,30 @@ export async function handleRoute({
|
|||
// By default, we should give priority to the status code passed, although it's possible that
|
||||
// the `Response` emitted by the user is a redirect. If so, then return the returned response.
|
||||
if (response.status < 400 && response.status >= 300) {
|
||||
if (
|
||||
response.status >= 300 &&
|
||||
response.status < 400 &&
|
||||
routeIsRedirect(route) &&
|
||||
!config.build.redirects &&
|
||||
pipeline.settings.buildOutput === 'static'
|
||||
) {
|
||||
// If we're here, it means that the calling static redirect that was configured by the user
|
||||
// We try to replicate the same behaviour that we provide during a static build
|
||||
response = new Response(
|
||||
redirectTemplate({
|
||||
status: response.status,
|
||||
location: response.headers.get('location')!,
|
||||
from: pathname,
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
...response.headers,
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
await writeSSRResult(request, response, incomingResponse);
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue