Rendering HTML Forms
Form-based flows (login, contact, settings) need a page to render the form and an endpoint to handle the POST. The pattern: GET renders the template with initial values, POST receives Form fields, validates them, and redirects back (or renders an error) - the classic Post/Redirect/Get flow.
The Form Template
<!doctype html>
<html>
<body>
<h1>Subscribe</h1>
{% if error %}<p class="error">{{ error }}</p>{% endif %}
<form method="post" action="/subscribe">
<label>Email <input name="email" value="{{ email | default('') }}" required /></label>
<label>Plan
<select name="plan">
<option value="free">Free</option>
<option value="pro">Pro</option>
</select>
</label>
<button>Submit</button>
</form>
</body>
</html>
GET and POST Endpoints
from fastapi import FastAPI, Form, Request
from fastapi.responses import RedirectResponse
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/subscribe")
def subscribe_page(request: Request):
return templates.TemplateResponse("subscribe.html", {"request": request})
@app.post("/subscribe")
def subscribe_submit(
request: Request,
email: str = Form(),
plan: str = Form("free"),
):
if not email or "@" not in email:
return templates.TemplateResponse(
"subscribe.html",
{"request": request, "error": "Valid email required", "email": email},
status_code=422,
)
# save the subscription
return RedirectResponse("/thanks", status_code=303)
Post/Redirect/Get
After a successful POST, redirect (303) to a confirmation page instead of re-rendering the form. If the user refreshes, the browser re-GETs the confirmation page rather than re-submitting the form - no duplicate submissions.
A Word on CSRF
For state-changing forms behind a session cookie, add a hidden token field the server validates on POST. Without a session (pure JSON APIs), CSRF is less of a concern because the browser does not attach credentials automatically.
Summary
Build form flows with a GET endpoint that renders the template and a POST endpoint that reads Form fields, validates them, and either re-renders with errors or redirects to a confirmation page. Use Post/Redirect/Get to prevent duplicate submissions, and add a CSRF token when the app relies on session cookies.