Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ Examples for BlackSheep.
| [./otel](./otel/) | Shows how to use [OpenTelemetry](https://opentelemetry.io/) integration with [Grafana](https://grafana.com/), and with [Azure Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview). |
| [./multipart](./multipart/) | Shows how to define custom classes to handle multipart/form-data input. |
| [./a2wsgi](./a2wsgi/) | Shows an example to use [`a2wsgi`](https://github.com/abersheeran/a2wsgi) with [`Gunicorn`](https://gunicorn.org/) or [`uWSGI`](https://uwsgi-docs.readthedocs.io/en/latest/). |
| [./content-types](./content-types/) | Shows how multiple content-types can be used and documented since version `2.6.2`. |
94 changes: 94 additions & 0 deletions content-types/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Content Types Example

This example demonstrates how to handle requests with multiple content types in
[BlackSheep](https://github.com/Neoteroi/BlackSheep) (requires **BlackSheep >= 2.6.2**).

For context, see https://github.com/Neoteroi/BlackSheep/pull/669 and https://github.com/Neoteroi/BlackSheep/issues/514.

## Overview

The `POST /foo` endpoint accepts request bodies in any of the following formats:

- `application/json`
- `application/xml`
- `text/xml`

This is achieved using a union type annotation combining `FromJSON` and `FromXML`
binders:

```python
@post("/foo")
async def create_foo(data: FromJSON[CreateFooInput] | FromXML[CreateFooInput]):
return Foo(data.foo)
```

BlackSheep inspects the `Content-Type` header of the incoming request and
automatically deserializes the body into the appropriate type, so the handler
receives a plain `CreateFooInput` instance regardless of which format the client
used.

## Running the example

```bash
pip install blacksheep>=2.6.2 uvicorn
python server.py
```

The OpenAPI documentation is available at `http://localhost:8000/docs` once the
server is running.

## Generated OpenAPI specification

```yaml
openapi: 3.1.0
info:
title: Example API
version: 0.0.1
paths:
/foo:
get:
responses:
'200':
description: Success response
content:
application/json:
schema:
$ref: '#/components/schemas/Foo'
operationId: get_foo
post:
responses: {}
operationId: create_foo
parameters: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateFooInput'
application/xml:
schema:
$ref: '#/components/schemas/CreateFooInput'
text/xml:
schema:
$ref: '#/components/schemas/CreateFooInput'
required: true
servers: []
components:
schemas:
Foo:
type: object
required:
- foo
properties:
foo:
type: string
nullable: false
CreateFooInput:
type: object
required:
- foo
properties:
foo:
type: string
nullable: false
tags: []
```
3 changes: 3 additions & 0 deletions content-types/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
blacksheep>=2.6.2
defusedxml==0.7.1
uvicorn==0.41.0
35 changes: 35 additions & 0 deletions content-types/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from dataclasses import dataclass

from blacksheep import Application, get, post, FromJSON, FromXML
from blacksheep.server.openapi.v3 import OpenAPIHandler
from openapidocs.v3 import Info

app = Application()

docs = OpenAPIHandler(info=Info(title="Example API", version="0.0.1"))
docs.bind_app(app)


@dataclass
class Foo:
foo: str


@dataclass
class CreateFooInput:
foo: str


@get("/foo")
async def get_foo() -> Foo:
return Foo("Hello!")


@post("/foo")
async def create_foo(data: FromJSON[CreateFooInput] | FromXML[CreateFooInput]):
return Foo(data.foo)


if __name__ == "__main__":
import uvicorn
uvicorn.run(app)