Skip to content

X-WWW-Form-Urlencoded

Reading the request body as application/x-www-form-urlencoded.

Description

X-WWW-Form-Urlencoded (MIME-type: application/x-www-form-urlencoded) is a widely used content type for transmitting data through HTML forms on the internet.

The format represents a string with key-value pairs as follows: key1=value1&key2=value2.

from pydantic import BaseModel
from rapidy.http import post, Body, ContentType

class UserData(BaseModel):
    username: str
    password: str

@post('/')
async def handler(
    user_data: UserData = Body(content_type=ContentType.x_www_form),
    # or
    user_data: UserData = Body(content_type='application/x-www-form-urlencoded'),
) -> ...:
Sending with curl
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=User&password=myAwesomePass" \
http://127.0.0.1:8080

Extracting Without Validation

Disabling validation is not recommended.

If validation is disabled, the parameter will contain a basic aiohttp structure:

  • Body(content_type=ContentType.x_www_form)MultiDictProxy[str]

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.x_www_form),
) -> ...:

Using Any

@post('/')
async def handler(
    data: Any = Body(content_type=ContentType.x_www_form),
) -> ...:

No Type Annotation

If no type is specified, Any will be set by default.

@post('/')
async def handler(
    data = Body(content_type=ContentType.x_www_form),
) -> ...:

Default Values

If the HTTP request does not contain a body, the parameter will receive the specified default value (if provided).

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.x_www_form),
    # or
    data: BodyData = Body(default_factory=lambda: 'some_data', content_type=ContentType.x_www_form),
) -> ...:

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.x_www_form),
    # or
    data: Optional[BodyData] = Body(content_type=ContentType.x_www_form),
    # or
    data: Union[BodyData, None] = Body(content_type=ContentType.x_www_form),
) -> ...:

Extracting Raw Data

Rapidy uses the post method of the Request object to retrieve data and passes it to Pydantic for validation.

How data extraction works in Rapidy

async def extract_post_data(request: Request) -> Optional[MultiDictProxy[Union[str, bytes, FileField]]]:
    if not request.body_exists:
        return None

    return await request.post()

Rapidy uses built-in aiohttp mechanisms.

For more information about the aiohttp.Request object and data extraction methods, see here.

x-www-form-urlencoded and multipart/form-data are handled the same way.

Both data types are extracted using the post method of the Request object. This is a characteristic of aiohttp implementation.

If a parameter is annotated as bytes or StreamReader, data is extracted differently.

For more details on the StreamReader object, see here.

bytes

@post('/')
async def handler(
    user_data: bytes = Body(content_type=ContentType.x_www_form),
    # also you can use pydantic validation
    user_data: bytes = Body(content_type=ContentType.x_www_form, min_length=1),
) -> ...:
Rapidy Internal Code
async def extract_body_bytes(request: Request) -> Optional[bytes]:
    if not request.body_exists:
        return None

    return await request.read()

StreamReader

from rapidy import StreamReader

@post('/')
async def handler(
    user_data: StreamReader = Body(content_type=ContentType.x_www_form),
) -> ...:
Rapidy Internal Code
async def extract_body_stream(request: Request) -> Optional[StreamReader]:
    if not request.body_exists:
        return None

    return request.content

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.

from rapidy import StreamReader

@post('/')
async def handler(
    user_data: StreamReader = Body(content_type=ContentType.x_www_form),
) -> ...:
------------------------------
Handler attribute with Type `Body` cannot have a default value.

Handler path: `<full_path>/main.py`
Handler name: `handler`
Attribute name: `data`
------------------------------