Skip to content

Forms

To extract forms in Xpresso, you start by declaring a Pydantic model to unpack the form into. The fields of the model correspond to the fields of the form data.

from typing import List

from pydantic import BaseModel

from xpresso import App, FromFormData, FromFormField, Path


class SearchForm(BaseModel):
    name: str  # implicit FromFormField[str]
    tags: FromFormField[List[str]]


async def log_search(form: FromFormData[SearchForm]) -> str:
    return f"{form.name} searched for {', '.join(form.tags)}"


app = App(routes=[Path(path="/form", post=log_search)])

This request extracts a application/x-www-form-urlencoded request into a FormModel object.

Form serialization

Xpresso fully supports the OpenAPI parameter serialization standard. You can customize deserialization using the style and explode keyword arguments to FormField():

from typing import List

from pydantic import BaseModel

from xpresso import App, FormField, FromFormData, Path
from xpresso.typing import Annotated


class SearchForm(BaseModel):
    name: str  # implicit FromFormField[str]
    tags: Annotated[List[str], FormField(explode=False)]


async def log_search(form: FromFormData[SearchForm]) -> str:
    return f"{form.name} searched for {', '.join(form.tags)}"


app = App(routes=[Path(path="/form", post=log_search)])

Multipart requests

Multipart requests (multipart/form-data) can be parsed almost identically to application/x-www-form-urlencoded. You can't upload mixed files and data in an application/x-www-form-urlencoded request, so you'll need to use a Multipart request. Multipart requests even support multiple files:

from typing import List

from pydantic import BaseModel

from xpresso import App, FromFormFile, FromMultipart, Path, UploadFile


class UploadForm(BaseModel):
    name: str  # implicit FromFormField[str]
    files: FromFormFile[List[UploadFile]]


async def log_search(form: FromMultipart[UploadForm]) -> str:
    data = [(await f.read()).decode() for f in form.files]
    return f"{form.name} uploaded {', '.join(data)}"


app = App(routes=[Path(path="/form", post=log_search)])

Tip

Fields in a application/x-www-form-urlencoded or multipart/form-data request can be repeated. This just means that a field of the same name appears more than once in the request. Often this is used to upload multiple files, such as in the example above.