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:
parent
9aaf58c133
commit
efded2a0b4
2 changed files with 40 additions and 14 deletions
|
@ -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 });
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue