# HTML form: GET the form, POST it, redirect on success The classic Post/Redirect/Get pattern, implemented with EDRI's HTML API: 1. `GET /contact` — renders an empty form. 2. `POST /contact` — validates input, stores it, then issues a `RedirectResponseDirective` to send the browser to a thank-you page. 3. `GET /contact/thanks` — renders confirmation. The point of the redirect is to prevent the browser from re-submitting on refresh. --- ## Events ```python # events/contact.py from http import HTTPMethod from edri.api.dataclass.api_event import api, ApiType from edri.dataclass.event import Event, event from edri.dataclass.response import Response, response from edri.utility.function import inject from edri.utility.validation import StringValidator @response class ContactFormResponse(Response): error: str | None @response class ContactThanksResponse(Response): pass @api( url="/contact", template="contact/form.j2", method=HTTPMethod.GET, exclude={ApiType.WS}, ) class ContactForm(Event): response: ContactFormResponse @api( url="/contact", template="contact/form.j2", # rendered only if validation fails method=HTTPMethod.POST, exclude={ApiType.WS}, ) class ContactSubmit(Event): name: inject(StringValidator, minimum_length=1, maximum_length=80) message: inject(StringValidator, minimum_length=1, maximum_length=2000) response: ContactFormResponse @api( url="/contact/thanks", template="contact/thanks.j2", method=HTTPMethod.GET, exclude={ApiType.WS}, ) class ContactThanks(Event): response: ContactThanksResponse ``` --- ## Manager ```python # managers/contact.py from edri.abstract import ManagerBase, request from edri.dataclass.directive.html import RedirectResponseDirective from edri.dataclass.response import ResponseStatus from events.contact import ContactForm, ContactSubmit, ContactThanks class ContactManager(ManagerBase): def __init__(self, router_queue): super().__init__(router_queue) self._messages: list[dict] = [] @request def show_form(self, event: ContactForm) -> None: event.response.error = None @request def submit(self, event: ContactSubmit) -> None: # `StringValidator` already enforced length, so this is a stand-in # for *business* validation that wasn't worth a custom validator. if "@" in event.name: event.response.error = "Please enter a name, not an email." event.response.set_status(ResponseStatus.FAILED) return self._messages.append({"name": event.name, "message": event.message}) # PRG: tell the browser to GET /contact/thanks. event.response.set_status(ResponseStatus.OK) event.response.add_directive(RedirectResponseDirective(ContactThanks)) @request def thanks(self, event: ContactThanks) -> None: # Nothing to fill in — the template is static. event.response.set_status(ResponseStatus.OK) ``` A few things worth pointing out: * `RedirectResponseDirective(ContactThanks)` takes the event **class**, not a URL. The HTML layer expands it to the URL declared on `ContactThanks.@api(url=...)`. If the target event had path placeholders, you'd pass them as keyword arguments (`RedirectResponseDirective(UserPage, user_id=42)`). * When validation succeeds, the directive overrides the default `200` rendering with a `302` redirect; the template is not used. * When validation fails, no directive is added — the template `contact/form.j2` renders and can show `data.error`. --- ## Templates `templates/contact/form.j2`: ```html

Contact us

{% if data.error %}

{{ data.error }}

{% endif %}


``` `templates/contact/thanks.j2`: ```html

Thanks!

Your message was received.

Send another ``` The `{% url 'EventName' %}` tag turns the event class name into the URL declared on its `@api(...)` decorator — see {ref}`url-generation-in-templates`. --- ## Entry point ```python # main.py from edri import EDRI from managers.contact import ContactManager if __name__ == "__main__": edri = EDRI() edri.add_component(ContactManager(edri.router_queue)) edri.start_api() edri.run() ``` Make sure `EDRI_TEMPLATE_PATH` points at the directory containing your `contact/` folder (default: `templates/`). See {ref}`templates-and-assets`. --- ## Trying it ```bash # render the form curl -sL http://localhost:8080/contact # submit curl -sL -X POST http://localhost:8080/contact \ -d "name=Alice&message=Hello" # -> 302 Location: /contact/thanks -> the thanks page # submit with a validation failure curl -sL -X POST http://localhost:8080/contact \ -d "name=a@b.c&message=oops" # -> 200 with the form, response.error rendered ```