Multipart Form Data
Reading the request body as multipart/form-data.
Description
Form Data (MIME-type: multipart/form-data) is one of the most commonly used content types for sending binary data to a server.
The multipart format means that data is sent to the server in separate parts. Each part can have its own content type, filename, and data. Data is separated using a boundary string.
from pydantic import BaseModel, ConfigDict
from rapidy.http import post, Body, ContentType, FileField
class UserData(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
username: str
password: str
image: FileField
@post('/')
async def handler(
user_data: UserData = Body(content_type=ContentType.m_part_form_data),
# or
user_data: UserData = Body(content_type='multipart/form-data'),
) -> ...:
Data Example
POST / HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=---WD9146A
Content-Length: ...
---WD9146A
Content-Disposition: form-data; name="username"
User
---WD9146A
Content-Disposition: form-data; name="password"
myAwesomePass
---WD9146A
Content-Disposition: form-data; name="image"; filename="image.png";
Content-Type: image/png
<... binary data ...>
---WD9146A
Sending with curl
Extraction Without Validation
Disabling validation is not recommended.
If validation is disabled, the parameter will contain the base aiohttp structure:
Body(content_type=ContentType.m_part_form_data) → MultiDictProxy[Union[str, bytes, FileField]]
Ways to Disable Validation
Explicit Disabling
from pydantic import BaseModel
from rapidy.http import post, Body, ContentType
class BodyData(BaseModel):
...
@post('/')
async def handler(
data: BodyData = Body(validate=False, content_type=ContentType.m_part_form_data),
) -> ...:
Using Any
@post('/')
async def handler(
data: Any = Body(content_type=ContentType.m_part_form_data),
) -> ...:
No Type Annotation
If no type is specified, Any will be set by default.
Default Values
If an HTTP request does not contain a body, the parameter will receive the specified default value (if set).
Usage Examples
Default Value Specified
from pydantic import BaseModel
from rapidy.http import post, Body, ContentType
class BodyData(BaseModel):
...
@post('/')
async def handler(
data: BodyData = Body('some_data', content_type=ContentType.m_part_form_data),
# or
data: BodyData = Body(default_factory=lambda: 'some_data', content_type=ContentType.m_part_form_data),
) -> ...:
Optional Request Body
from pydantic import BaseModel
from rapidy.http import post, Body, ContentType
class BodyData(BaseModel):
...
@post('/')
async def handler(
data: BodyData | None = Body(content_type=ContentType.m_part_form_data),
# or
data: Optional[BodyData] = Body(content_type=ContentType.m_part_form_data),
# or
data: Union[BodyData, None] = Body(content_type=ContentType.m_part_form_data),
) -> ...:
Extracting Raw Data
Rapidy uses the post method of the Request object to obtain data and passes it to Pydantic for validation.
How data extraction works in Rapidy
Rapidy uses built-in aiohttp mechanisms for data extraction.
More details about the aiohttp.Request object and methods for extracting data from it can be found
here.
x-www-form-urlencoded and multipart/form-data are processed the same way.
Both of these content types are extracted using the post method of the Request object. This is a feature of aiohttp.
If a parameter is annotated as bytes or StreamReader, data is extracted differently.
More details about the StreamReader object can be found here.
bytes
from rapidy.http import post, Body, ContentType
@post('/')
async def handler(
user_data: bytes = Body(content_type=ContentType.m_part_form_data),
# also you can use pydantic validation
user_data: bytes = Body(content_type=ContentType.m_part_form_data, min_length=1),
) -> ...:
Rapidy Internal Code
StreamReader
from rapidy import StreamReader
from rapidy.http import post, Body, ContentType
@post('/')
async def handler(
user_data: StreamReader = Body(content_type=ContentType.m_part_form_data),
) -> ...:
Rapidy Internal Code
Validation with Pydantic is not supported for StreamReader.
Default values cannot be set for StreamReader.
If you attempt to set a default value for Body with a StreamReader annotation using default or default_factory,
a ParameterCannotUseDefaultError will be raised.