Source code for falcon.media.urlencoded

from __future__ import annotations

from typing import Any, Optional
from urllib.parse import urlencode

from falcon import errors
from falcon.media.base import BaseHandler
from falcon.typing import AsyncReadableIO
from falcon.typing import ReadableIO
from falcon.util.uri import parse_query_string


[docs] class URLEncodedFormHandler(BaseHandler): """URL-encoded form data handler. This handler parses ``application/x-www-form-urlencoded`` HTML forms to a ``dict``, similar to how URL query parameters are parsed. An empty body will be parsed as an empty dict. When deserializing, this handler will raise :class:`falcon.MediaMalformedError` if the request payload cannot be parsed as ASCII or if any of the URL-encoded strings in the payload are not valid UTF-8. As documented for :any:`urllib.parse.urlencode`, when serializing, the media object must either be a ``dict`` or a sequence of two-element ``tuple``'s. If any values in the media object are sequences, each sequence element is converted to a separate parameter. Keyword Arguments: keep_blank (bool): Whether to keep empty-string values from the form when deserializing. csv (bool): Whether to split comma-separated form values into list when deserializing. """ def __init__(self, keep_blank: bool = True, csv: bool = False) -> None: self._keep_blank = keep_blank self._csv = csv # NOTE(kgriffs): To be safe, only enable the optimized protocol when # not subclassed. if type(self) is URLEncodedFormHandler: self._serialize_sync = self.serialize self._deserialize_sync = self._deserialize # NOTE(kgriffs): Make content_type a kwarg to support the # Request.render_body() shortcut optimization. def serialize(self, media: Any, content_type: Optional[str] = None) -> bytes: # NOTE(vytas): Setting doseq to True to mirror the parse_query_string # behaviour. return urlencode(media, doseq=True).encode() def _deserialize(self, body: bytes) -> Any: try: # NOTE(kgriffs): According to # https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#application%2Fx-www-form-urlencoded-encoding-algorithm # the # body should be US-ASCII. Enforcing this also helps # catch malicious input. body_str = body.decode('ascii') return parse_query_string( body_str, keep_blank=self._keep_blank, csv=self._csv ) except Exception as err: raise errors.MediaMalformedError('URL-encoded') from err def deserialize( self, stream: ReadableIO, content_type: Optional[str], content_length: Optional[int], ) -> Any: return self._deserialize(stream.read()) async def deserialize_async( self, stream: AsyncReadableIO, content_type: Optional[str], content_length: Optional[int], ) -> Any: return self._deserialize(await stream.read())