0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00
astro/.changeset/tall-waves-impress.md
Ben Holmes d10f91815e
Actions middleware (#12373)
* add manual middleware config option with getMiddlewareContext()

* refactor requestInfo to action object

* set action error response status from render context

* update automatic middleware to plain POST handler

* fix missing Locals type

* test: add separate POST and cookie forwarding tests

* remove actions.middleware flag

* add docs on actionResultAlreadySet

* test: use Astro.rewrite instead of middleware next(). TODO: fix next()

* fix type errors from rebase

* test: remove middleware handler

* test: use cookie forwarding for 'lots of fields'

* refactor: _isPrerendered -> ctx.isPrerendered

* expose getOriginPathname as middleware utility

* add support for handling RPC action results from middleware

* test: RPC security middleware

* refactor POST route handler to use getMiddlewareContext()

* remove unused actionRedirect flag

* changeset

* test: add expectedd rewrite failure for Ema to debug

* fix e2e test

* nit: form -> from

Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>

* rename getMiddlewareContext -> getActionContext

* rename form-action -> form

* move /_actions/ route pattern to const

* move type defs to user-accessible ActionMiddlewareContext type

* export action middleware context type

* strip omitted fields for Action API Context

* add satisfies to type for good measure

* move getOriginPathname to shared ctx.originPathname

* remove `next()` rewrite because it isn't supported

* fix empty forms raising a 415

* fix missing async on cookie example

* nit: ctx -> context

* fix json parse error when content length is 0

* refactor body parsing to function

* edit: migration -> updating your HTML form actions

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>

* update changeset to match docs v5 guide

* add absolute urls to changeset links

---------

Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
2024-11-08 17:03:57 -05:00

2.7 KiB

astro
minor

Changes the default behavior for Astro Action form requests to a standard POST submission.

In Astro 4.x, actions called from an HTML form would trigger a redirect with the result forwarded using cookies. This caused issues for large form errors and return values that exceeded the 4 KB limit of cookie-based storage.

Astro 5.0 now renders the result of an action as a POST result without any forwarding. This will introduce a "confirm form resubmission?" dialog when a user attempts to refresh the page, though it no longer imposes a 4 KB limit on action return value.

Customize form submission behavior

If you prefer to address the "confirm form resubmission?" dialog on refresh, or to preserve action results across sessions, you can now customize action result handling from middleware.

We recommend using a session storage provider as described in our Netlify Blob example. However, if you prefer the cookie forwarding behavior from 4.X and accept the 4 KB size limit, you can implement the pattern as shown in this sample snippet:

// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';
import { getActionContext } from 'astro:actions';

export const onRequest = defineMiddleware(async (context, next) => {
  // Skip requests for prerendered pages
  if (context.isPrerendered) return next();

	const { action, setActionResult, serializeActionResult } = getActionContext(context);

	// If an action result was forwarded as a cookie, set the result
	// to be accessible from `Astro.getActionResult()`
	const payload = context.cookies.get('ACTION_PAYLOAD');
	if (payload) {
		const { actionName, actionResult } = payload.json();
		setActionResult(actionName, actionResult);
		context.cookies.delete('ACTION_PAYLOAD');
		return next();
	}

	// If an action was called from an HTML form action,
	// call the action handler and redirect with the result as a cookie.
	if (action?.calledFrom === 'form') {
		const actionResult = await action.handler();

		context.cookies.set('ACTION_PAYLOAD', {
			actionName: action.name,
			actionResult: serializeActionResult(actionResult),
		});

		if (actionResult.error) {
		// Redirect back to the previous page on error
			const referer = context.request.headers.get('Referer');
			if (!referer) {
				throw new Error('Internal: Referer unexpectedly missing from Action POST request.');
			}
			return context.redirect(referer);
		}
		// Redirect to the destination page on success
		return context.redirect(context.originPathname);
	}

	return next();
})