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:
parent
d10f91815e
commit
bdb75a87f2
7 changed files with 88 additions and 0 deletions
7
.changeset/proud-games-repair.md
Normal file
7
.changeset/proud-games-repair.md
Normal 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.
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
4
packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/dynamic.astro
vendored
Normal file
4
packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/dynamic.astro
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
return Astro.rewrite("/forbidden/static")
|
||||||
|
export const prerender = false
|
||||||
|
---
|
3
packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/static.astro
vendored
Normal file
3
packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/static.astro
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
export const prerender = true
|
||||||
|
---
|
|
@ -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', () => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue