Skip to content

Fix #514#669

Merged
RobertoPrevato merged 6 commits intomainfrom
fix/514
Feb 24, 2026
Merged

Fix #514#669
RobertoPrevato merged 6 commits intomainfrom
fix/514

Conversation

@RobertoPrevato
Copy link
Member

@RobertoPrevato RobertoPrevato commented Feb 24, 2026

  • Fix #514 — add support for
    multiple request body formats on a single endpoint.
    • New MultiFormatBodyBinder: holds an ordered list of inner BodyBinders and
      dispatches get_value to the first whose matches_content_type returns True.
      Returns HTTP 415 Unsupported Media Type when the binder is required and no
      inner binder matches the request's Content-Type.

    • Union annotation syntax — the normalization layer detects a union of
      body-binder BoundValue types and automatically builds a
      MultiFormatBodyBinder:

      async def create_item(data: FromJSON[Item] | FromForm[Item]) -> Item: ...
      # optional variant
      async def create_item(data: FromJSON[Item] | FromForm[Item] | None) -> Item: ...
    • New FromBody[T] convenience type: equivalent to
      FromJSON[T] | FromForm[T], useful when you want to accept both structured
      formats without being explicit:

      async def create_item(data: FromBody[Item]) -> Item: ...
    • New FromXML[T] and XMLBinder: parse application/xml / text/xml
      request bodies using defusedxml,
      protecting against XXE injection, entity expansion (billion laughs),
      and DTD-based attacks. Security exceptions propagate unmodified so the
      application can distinguish attack attempts from ordinary malformed input.
      Install the extra with pip install blacksheep[xml].

    • All new types compose freely:

      async def create_item(data: FromJSON[Item] | FromXML[Item] | FromForm[Item]): ...
    • OpenAPI Specification is generated correctly for all combinations: each
      accepted content type appears as a separate entry under requestBody.content,
      all referencing the same schema.

See https://github.com/Neoteroi/BlackSheep-Examples/tree/main/content-types

RobertoPrevato and others added 6 commits February 24, 2026 19:01
- Add UnsupportedMediaType (HTTP 415) exception to blacksheep/exceptions.py
- Add FromBody[T] BoundValue type: accepts JSON and form bodies by default
- Add MultiFormatBodyBinder: holds a list of inner BodyBinders, delegates
  get_value to the first whose matches_content_type returns True; raises
  UnsupportedMediaType (415) when required and no inner binder matches
- Add FromBodyBinder: the concrete binder backing FromBody[T]
- Normalization: detect union-of-BodyBinder annotations such as
  FromJSON[T] | FromForm[T] and automatically build a MultiFormatBodyBinder;
  optional unions (FromJSON[T] | FromForm[T] | None) set required=False
- Export FromBody from the top-level blacksheep package
- OpenAPI: existing _get_body_binder_content_type split-on-semicolon logic
  handles MultiFormatBodyBinder transparently via its content_type property

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Verify that FromJSON[T] | FromForm[T] and FromBody[T] both emit all
accepted content types in the requestBody, and that the optional
variant emits required: false.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add FromXML[T] BoundValue type; exported from top-level blacksheep
- Add XMLBinder: parses application/xml and text/xml bodies using
  defusedxml, protecting against XXE injection, entity expansion
  (billion laughs), and DTD-based attacks
- defusedxml security exceptions (DTDForbidden, EntitiesForbidden, …)
  propagate unmodified; genuine XML parse errors become InvalidRequestBody
- Add _element_to_dict() helper: strips namespaces, collects repeated
  sibling tags as lists, merges element attributes
- ClassConverter already coerces string values to typed fields (int,
  float, UUID, datetime, …), so XML parsing works end-to-end with
  the same converter chain as JSON and form binders
- Add defusedxml>=0.7.1 to [xml] and [full] optional extras in
  pyproject.toml
- FromXML participates in the MultiFormatBodyBinder union syntax:
  FromJSON[T] | FromXML[T] works out of the box
- Add XMLBinder tests: parsing, content-type matching, malformed XML,
  XXE and billion-laughs rejection, _element_to_dict edge cases
- Add OpenAPI tests: FromXML[T] emits application/xml + text/xml;
  FromJSON[T] | FromXML[T] emits all three content types

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…L support

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@RobertoPrevato RobertoPrevato merged commit 7b8df5b into main Feb 24, 2026
16 checks passed
@RobertoPrevato RobertoPrevato deleted the fix/514 branch February 24, 2026 21:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ContentInfo Type to OpenAPI EndpointDocs.request_body, so docs can test different types of content-type requests.

1 participant