0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-02-03 22:29:08 -05:00

fix(routing): emit error for forbidden rewrite (#12339)

Co-authored-by: Reuben Tier <64310361+TheOtterlord@users.noreply.github.com>
Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com>
Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev>
Co-authored-by: Reuben Tier <otterlord.dev@gmail.com>
Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com>
This commit is contained in:
Emanuele Stoppa 2024-11-11 15:03:27 +00:00 committed by GitHub
parent d10f91815e
commit bdb75a87f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 88 additions and 0 deletions

View file

@ -0,0 +1,7 @@
---
'astro': patch
---
Adds an error when `Astro.rewrite()` is used to rewrite an on-demand route with a static route when using the `"server"` output.
This is a forbidden rewrite because Astro can't retrieve the emitted static route at runtime. This route is served by the hosting platform, and not Astro itself.

View file

@ -1260,6 +1260,23 @@ export const RewriteWithBodyUsed = {
'Astro.rewrite() cannot be used if the request body has already been read. If you need to read the body, first clone the request.', 'Astro.rewrite() cannot be used if the request body has already been read. If you need to read the body, first clone the request.',
} satisfies ErrorData; } satisfies ErrorData;
/**
* @docs
* @description
* `Astro.rewrite()` can't be used to rewrite an on-demand route with a static route when using the `"server"` output.
*
*/
export const ForbiddenRewrite = {
name: 'ForbiddenRewrite',
title: 'Forbidden rewrite to a static route.',
message: (from: string, to: string, component: string) =>
`You tried to rewrite the on-demand route '${from}' with the static route '${to}', when using the 'server' output. \n\nThe static route '${to}' is rendered by the component
'${component}', which is marked as prerendered. This is a forbidden operation because during the build the component '${component}' is compiled to an
HTML file, which can't be retrieved at runtime by Astro.`,
hint: (component: string) =>
`Add \`export const prerender = false\` to the component '${component}', or use a Astro.redirect().`,
} satisfies ErrorData;
/** /**
* @docs * @docs
* @description * @description

View file

@ -1,6 +1,8 @@
import type { MiddlewareHandler, RewritePayload } from '../../types/public/common.js'; import type { MiddlewareHandler, RewritePayload } from '../../types/public/common.js';
import type { APIContext } from '../../types/public/context.js'; import type { APIContext } from '../../types/public/context.js';
import { AstroCookies } from '../cookies/cookies.js'; import { AstroCookies } from '../cookies/cookies.js';
import { ForbiddenRewrite } from '../errors/errors-data.js';
import { AstroError } from '../errors/index.js';
import { apiContextRoutesSymbol } from '../render-context.js'; import { apiContextRoutesSymbol } from '../render-context.js';
import { type Pipeline, getParams } from '../render/index.js'; import { type Pipeline, getParams } from '../render/index.js';
import { defineMiddleware } from './index.js'; import { defineMiddleware } from './index.js';
@ -49,6 +51,22 @@ export function sequence(...handlers: MiddlewareHandler[]): MiddlewareHandler {
payload, payload,
handleContext.request, handleContext.request,
); );
// This is a case where the user tries to rewrite from a SSR route to a prerendered route (SSG).
// This case isn't valid because when building for SSR, the prerendered route disappears from the server output because it becomes an HTML file,
// so Astro can't retrieve it from the emitted manifest.
if (
pipeline.serverLike === true &&
handleContext.isPrerendered === false &&
routeData.prerender === true
) {
throw new AstroError({
...ForbiddenRewrite,
message: ForbiddenRewrite.message(pathname, pathname, routeData.component),
hint: ForbiddenRewrite.hint(routeData.component),
});
}
carriedPayload = payload; carriedPayload = payload;
handleContext.request = newRequest; handleContext.request = newRequest;
handleContext.url = new URL(newRequest.url); handleContext.url = new URL(newRequest.url);

View file

@ -23,6 +23,7 @@ import {
} from './constants.js'; } from './constants.js';
import { AstroCookies, attachCookiesToResponse } from './cookies/index.js'; import { AstroCookies, attachCookiesToResponse } from './cookies/index.js';
import { getCookiesFromResponse } from './cookies/response.js'; import { getCookiesFromResponse } from './cookies/response.js';
import { ForbiddenRewrite } from './errors/errors-data.js';
import { AstroError, AstroErrorData } from './errors/index.js'; import { AstroError, AstroErrorData } from './errors/index.js';
import { callMiddleware } from './middleware/callMiddleware.js'; import { callMiddleware } from './middleware/callMiddleware.js';
import { sequence } from './middleware/index.js'; import { sequence } from './middleware/index.js';
@ -145,6 +146,22 @@ export class RenderContext {
pathname, pathname,
newUrl, newUrl,
} = await pipeline.tryRewrite(payload, this.request); } = await pipeline.tryRewrite(payload, this.request);
// This is a case where the user tries to rewrite from a SSR route to a prerendered route (SSG).
// This case isn't valid because when building for SSR, the prerendered route disappears from the server output because it becomes an HTML file,
// so Astro can't retrieve it from the emitted manifest.
if (
this.pipeline.serverLike === true &&
this.routeData.prerender === false &&
routeData.prerender === true
) {
throw new AstroError({
...ForbiddenRewrite,
message: ForbiddenRewrite.message(this.pathname, pathname, routeData.component),
hint: ForbiddenRewrite.hint(routeData.component),
});
}
this.routeData = routeData; this.routeData = routeData;
componentInstance = newComponent; componentInstance = newComponent;
if (payload instanceof Request) { if (payload instanceof Request) {
@ -246,6 +263,21 @@ export class RenderContext {
reroutePayload, reroutePayload,
this.request, this.request,
); );
// This is a case where the user tries to rewrite from a SSR route to a prerendered route (SSG).
// This case isn't valid because when building for SSR, the prerendered route disappears from the server output because it becomes an HTML file,
// so Astro can't retrieve it from the emitted manifest.
if (
this.pipeline.serverLike === true &&
this.routeData.prerender === false &&
routeData.prerender === true
) {
throw new AstroError({
...ForbiddenRewrite,
message: ForbiddenRewrite.message(this.pathname, pathname, routeData.component),
hint: ForbiddenRewrite.hint(routeData.component),
});
}
this.routeData = routeData; this.routeData = routeData;
if (reroutePayload instanceof Request) { if (reroutePayload instanceof Request) {
this.request = reroutePayload; this.request = reroutePayload;

View file

@ -0,0 +1,4 @@
---
return Astro.rewrite("/forbidden/static")
export const prerender = false
---

View file

@ -0,0 +1,3 @@
---
export const prerender = true
---

View file

@ -149,6 +149,13 @@ describe('Dev rewrite, hybrid/server', () => {
assert.equal($('title').text(), 'RewriteWithBodyUsed'); assert.equal($('title').text(), 'RewriteWithBodyUsed');
}); });
it('should error when rewriting from a SSR route to a SSG route', async () => {
const html = await fixture.fetch('/forbidden/dynamic').then((res) => res.text());
const $ = cheerioLoad(html);
assert.match($('title').text(), /ForbiddenRewrite/);
});
}); });
describe('Build reroute', () => { describe('Build reroute', () => {