0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-04-14 23:51:49 -05:00

feat: redirect with result to avoid resubmission dialog

This commit is contained in:
bholmesdev 2024-08-02 13:44:43 -04:00
parent 9aaf58c133
commit efded2a0b4
2 changed files with 40 additions and 14 deletions

View file

@ -13,6 +13,7 @@ import {
type SerializedActionResult,
serializeActionResult,
} from './virtual/shared.js';
import type { AstroCookie } from '../../core/cookies/cookies.js';
export type Locals = {
_actionsInternal: {
@ -30,6 +31,11 @@ export const onRequest = defineMiddleware(async (context, next) => {
// so short circuit if already defined.
if (locals._actionsInternal) return next();
const actionResultCookie = context.cookies.get('_actionResult');
if (actionResultCookie) {
return renderResult({ context, next, actionResultCookie });
}
// 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
@ -60,6 +66,27 @@ export const onRequest = defineMiddleware(async (context, next) => {
return next();
});
async function renderResult({
context,
next,
actionResultCookie,
}: { context: APIContext; next: MiddlewareNext; actionResultCookie: AstroCookie }) {
const locals = context.locals as Locals;
locals._actionsInternal = actionResultCookie.json();
const response = await next();
context.cookies.delete('_actionResult');
if (locals._actionsInternal.actionResult.type === 'error') {
return new Response(response.body, {
status: locals._actionsInternal.actionResult.status,
statusText: locals._actionsInternal.actionResult.type,
headers: response.headers,
});
}
return response;
}
async function handlePost({
context,
next,
@ -83,10 +110,10 @@ async function handlePost({
const action = baseAction.bind(context);
const actionResult = await action(formData);
return handleResult({ context, next, actionName, actionResult });
return redirectWithResult({ context, next, actionName, actionResult });
}
async function handleResult({
async function redirectWithResult({
context,
next,
actionName,
@ -97,21 +124,20 @@ async function handleResult({
actionName: string;
actionResult: SafeResult<any, any>;
}) {
const locals = context.locals as Locals;
locals._actionsInternal = {
// TODO: encrypt the action result
context.cookies.set('_actionResult', {
actionName,
actionResult: serializeActionResult(actionResult),
};
});
const response = await next();
if (actionResult.error) {
return new Response(response.body, {
status: actionResult.error.status,
statusText: actionResult.error.type,
headers: response.headers,
});
const referer = context.request.headers.get('Referer');
if (!referer) return next();
return context.redirect(referer);
}
return response;
return context.redirect(context.url.pathname);
}
async function handlePostLegacy({ context, next }: { context: APIContext; next: MiddlewareNext }) {
@ -143,5 +169,5 @@ async function handlePostLegacy({ context, next }: { context: APIContext; next:
const action = baseAction.bind(context);
const actionResult = await action(formData);
return handleResult({ context, next, actionName, actionResult });
return redirectWithResult({ context, next, actionName, actionResult });
}

View file

@ -35,7 +35,7 @@ const DELETED_EXPIRATION = new Date(0);
const DELETED_VALUE = 'deleted';
const responseSentSymbol = Symbol.for('astro.responseSent');
class AstroCookie implements AstroCookieInterface {
export class AstroCookie implements AstroCookieInterface {
constructor(public value: string) {}
json() {
if (this.value === undefined) {