mirror of
https://github.com/withastro/astro.git
synced 2025-03-24 23:21:57 -05:00
[Actions] Fix middleware warning static mode (#11794)
* fix: remove static usage warning with isPrendered flag * fix(test): cookie is empty for prerendered routes in dev * chore: add test route * chore: changeset
This commit is contained in:
parent
7e2f142a5a
commit
3691a626fb
6 changed files with 81 additions and 26 deletions
5
.changeset/chatty-cups-sleep.md
Normal file
5
.changeset/chatty-cups-sleep.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes unexpected warning log when using Actions on "hybrid" rendered projects.
|
|
@ -17,14 +17,13 @@ export async function getStaticPaths() {
|
|||
}));
|
||||
}
|
||||
|
||||
|
||||
type Props = CollectionEntry<'blog'>;
|
||||
|
||||
const post = await getEntry('blog', Astro.params.slug)!;
|
||||
const { Content } = await post.render();
|
||||
|
||||
if (Astro.url.searchParams.has('like')) {
|
||||
await Astro.callAction(actions.blog.like.orThrow, {postId: post.id});
|
||||
await Astro.callAction(actions.blog.like.orThrow, { postId: post.id });
|
||||
}
|
||||
|
||||
const comment = Astro.getActionResult(actions.blog.comment);
|
||||
|
@ -57,17 +56,17 @@ const commentPostIdOverride = Astro.url.searchParams.get('commentPostIdOverride'
|
|||
/>
|
||||
<form method="POST" data-testid="progressive-fallback" action={actions.blog.comment.queryString}>
|
||||
<input type="hidden" name="postId" value={post.id} />
|
||||
<label for="fallback-author">
|
||||
Author
|
||||
</label>
|
||||
<input id="fallback-author" type="text" name="author" required />
|
||||
<label for="fallback-body" class="sr-only">
|
||||
Comment
|
||||
</label>
|
||||
<label for="fallback-author"> Author </label>
|
||||
<input id="fallback-author" type="text" name="author" required />
|
||||
<label for="fallback-body" class="sr-only"> Comment </label>
|
||||
<textarea id="fallback-body" rows={10} name="body" required></textarea>
|
||||
{isInputError(comment?.error) && comment.error.fields.body && (
|
||||
<p class="error" data-error="body">{comment.error.fields.body.toString()}</p>
|
||||
)}
|
||||
{
|
||||
isInputError(comment?.error) && comment.error.fields.body && (
|
||||
<p class="error" data-error="body">
|
||||
{comment.error.fields.body.toString()}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
<button type="submit">Post Comment</button>
|
||||
</form>
|
||||
<div data-testid="server-comments">
|
||||
|
|
|
@ -22,8 +22,18 @@ export type Locals = {
|
|||
};
|
||||
|
||||
export const onRequest = defineMiddleware(async (context, next) => {
|
||||
if ((context as any)._isPrerendered) {
|
||||
if (context.request.method === 'POST') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
yellow('[astro:actions]'),
|
||||
'POST requests should not be sent to prerendered pages. If you\'re using Actions, disable prerendering with `export const prerender = "false".',
|
||||
);
|
||||
}
|
||||
return next();
|
||||
}
|
||||
|
||||
const locals = context.locals as Locals;
|
||||
const { request } = context;
|
||||
// Actions middleware may have run already after a path rewrite.
|
||||
// See https://github.com/withastro/roadmap/blob/feat/reroute/proposals/0047-rerouting.md#ctxrewrite
|
||||
// `_actionPayload` is the same for every page,
|
||||
|
@ -38,16 +48,6 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|||
return renderResult({ context, next, ...actionPayload });
|
||||
}
|
||||
|
||||
// Heuristic: If body is null, Astro might've reset this for prerendering.
|
||||
if (import.meta.env.DEV && request.method === 'POST' && request.body === null) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
yellow('[astro:actions]'),
|
||||
'POST requests should not be sent to prerendered pages. If you\'re using Actions, disable prerendering with `export const prerender = "false".',
|
||||
);
|
||||
return next();
|
||||
}
|
||||
|
||||
const actionName = context.url.searchParams.get(ACTION_QUERY_PARAMS.actionName);
|
||||
|
||||
if (context.request.method === 'POST' && actionName) {
|
||||
|
@ -92,7 +92,11 @@ async function handlePost({
|
|||
context,
|
||||
next,
|
||||
actionName,
|
||||
}: { context: APIContext; next: MiddlewareNext; actionName: string }) {
|
||||
}: {
|
||||
context: APIContext;
|
||||
next: MiddlewareNext;
|
||||
actionName: string;
|
||||
}) {
|
||||
const { request } = context;
|
||||
|
||||
const baseAction = await getAction(actionName);
|
||||
|
|
|
@ -114,6 +114,8 @@ export class RenderContext {
|
|||
const { cookies, middleware, pipeline } = this;
|
||||
const { logger, serverLike, streaming } = pipeline;
|
||||
|
||||
const isPrerendered = !serverLike || this.routeData.prerender;
|
||||
|
||||
const props =
|
||||
Object.keys(this.props).length > 0
|
||||
? this.props
|
||||
|
@ -125,7 +127,7 @@ export class RenderContext {
|
|||
logger,
|
||||
serverLike,
|
||||
});
|
||||
const apiContext = this.createAPIContext(props);
|
||||
const apiContext = this.createAPIContext(props, isPrerendered);
|
||||
|
||||
this.counter++;
|
||||
if (this.counter === 4) {
|
||||
|
@ -212,12 +214,17 @@ export class RenderContext {
|
|||
return response;
|
||||
}
|
||||
|
||||
createAPIContext(props: APIContext['props']): APIContext {
|
||||
createAPIContext(props: APIContext['props'], isPrerendered: boolean): APIContext {
|
||||
const context = this.createActionAPIContext();
|
||||
return Object.assign(context, {
|
||||
props,
|
||||
getActionResult: createGetActionResult(context.locals),
|
||||
callAction: createCallAction(context),
|
||||
// Used internally by Actions middleware.
|
||||
// TODO: discuss exposing this information from APIContext.
|
||||
// middleware runs on prerendered routes in the dev server,
|
||||
// so this is useful information to have.
|
||||
_isPrerendered: isPrerendered,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import * as cheerio from 'cheerio';
|
|||
import * as devalue from 'devalue';
|
||||
import testAdapter from './test-adapter.js';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
import { serializeActionResult } from '../dist/actions/runtime/virtual/shared.js';
|
||||
|
||||
describe('Astro Actions', () => {
|
||||
let fixture;
|
||||
|
@ -25,6 +26,28 @@ describe('Astro Actions', () => {
|
|||
await devServer.stop();
|
||||
});
|
||||
|
||||
it('Does not process middleware cookie for prerendered routes', async () => {
|
||||
const cookie = new URLSearchParams();
|
||||
cookie.append(
|
||||
'_astroActionPayload',
|
||||
JSON.stringify({
|
||||
actionName: 'subscribe',
|
||||
actionResult: serializeActionResult({
|
||||
data: { channel: 'bholmesdev', subscribeButtonState: 'smashed' },
|
||||
error: undefined,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
const res = await fixture.fetch('/subscribe-prerendered', {
|
||||
headers: {
|
||||
Cookie: cookie.toString(),
|
||||
},
|
||||
});
|
||||
const html = await res.text();
|
||||
const $ = cheerio.load(html);
|
||||
assert.equal($('body').text().trim(), 'No cookie found.');
|
||||
});
|
||||
|
||||
it('Exposes subscribe action', async () => {
|
||||
const res = await fixture.fetch('/_actions/subscribe', {
|
||||
method: 'POST',
|
||||
|
|
17
packages/astro/test/fixtures/actions/src/pages/subscribe-prerendered.astro
vendored
Normal file
17
packages/astro/test/fixtures/actions/src/pages/subscribe-prerendered.astro
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
import { actions } from 'astro:actions';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
const result = Astro.getActionResult(actions.subscribe);
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>{result?.data?.subscribeButtonState ?? 'No cookie found.'}</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue