Request data#
Request body#
The body of HTTP requests can be accessed using the special data
parameter in a handler function.
from typing import Dict
from starlite import Starlite, post
@post(path="/")
async def index(data: Dict[str, str]) -> Dict[str, str]:
return data
app = Starlite(route_handlers=[index])
from starlite import Starlite, post
@post(path="/")
async def index(data: dict[str, str]) -> dict[str, str]:
return data
app = Starlite(route_handlers=[index])
The type of data
an be any supported type, including
Pydantic models
Arbitrary stdlib types
Typed supported via plugins
from dataclasses import dataclass
from starlite import Starlite, post
@dataclass
class User:
id: int
name: str
@post(path="/")
async def index(data: User) -> User:
return data
app = Starlite(route_handlers=[index])
Validation and customizing OpenAPI documentation#
With the help of Body
, you have fine-grained control over the validation
of the request body, and can also customize the OpenAPI documentation:
from dataclasses import dataclass
from starlite import Body, Starlite, post
@dataclass
class User:
id: int
name: str
@post(path="/")
async def create_user(
data: User = Body(title="Create User", description="Create a new user."),
) -> User:
return data
app = Starlite(route_handlers=[create_user])
Specifying a content-type#
By default, Starlite will try to parse the request body as JSON. While this may be desired
in most cases, you might want to specify a different type. You can do so by passing a
RequestEncodingType
to Body
. This will also
help to generate the correct media-type in the OpenAPI schema.
URL Encoded Form Data#
To access data sent as url-encoded form data,
i.e. application/x-www-form-urlencoded
Content-Type header, use Body
and specify
RequestEncodingType.URL_ENCODED
as the media_type
:
from dataclasses import dataclass
from starlite import Body, RequestEncodingType, Starlite, post
@dataclass
class User:
id: int
name: str
@post(path="/")
async def create_user(
data: User = Body(media_type=RequestEncodingType.URL_ENCODED),
) -> User:
return data
app = Starlite(route_handlers=[create_user])
Note
URL encoded data is inherently less versatile than JSON data - for example, it cannot handle complex dictionaries and deeply nested data. It should only be used for simple data structures.
MultiPart Form Data#
You can access data uploaded using a request with a
multipart/form-data
Content-Type header by specifying it in the Body
function:
from dataclasses import dataclass
from starlite import Body, RequestEncodingType, Starlite, post
@dataclass
class User:
id: int
name: str
@post(path="/")
async def create_user(
data: User = Body(media_type=RequestEncodingType.MULTI_PART),
) -> User:
return data
app = Starlite(route_handlers=[create_user])
File uploads#
In case of files uploaded, Starlite transforms the results into an instance
of UploadFile
class, which offer a convenient
interface for working with files. Therefore, you need to type your file uploads accordingly.
To access a single file simply type data
as UploadFile
:
from starlite import Body, MediaType, RequestEncodingType, Starlite, UploadFile, post
@post(path="/", media_type=MediaType.TEXT)
async def handle_file_upload(
data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART),
) -> str:
content = await data.read()
filename = data.filename
return f"{filename}, {content.decode()}"
app = Starlite(route_handlers=[handle_file_upload])
from starlite import Body, MediaType, RequestEncodingType, Starlite, UploadFile, post
@post(path="/", media_type=MediaType.TEXT)
def handle_file_upload(
data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART),
) -> str:
content = data.file.read()
filename = data.filename
return f"{filename}, {content.decode()}"
app = Starlite(route_handlers=[handle_file_upload])
Technical details
UploadFile
wraps
SpooledTemporaryFile
so it can be used asynchronously. Inside of a
synchronous function we don’t need this wrapper, so we can use
SpooledTemporaryFile.read
directly.
Multiple files#
To access multiple files with known filenames, you can use a pydantic model:
from typing import Dict
from pydantic import BaseConfig, BaseModel
from starlite import Body, RequestEncodingType, Starlite, UploadFile, post
class FormData(BaseModel):
cv: UploadFile
diploma: UploadFile
class Config(BaseConfig):
arbitrary_types_allowed = True
@post(path="/")
async def handle_file_upload(
data: FormData = Body(media_type=RequestEncodingType.MULTI_PART),
) -> Dict:
cv_content = await data.cv.read()
diploma_content = await data.diploma.read()
return {"cv": cv_content.decode(), "diploma": diploma_content.decode()}
app = Starlite(route_handlers=[handle_file_upload])
from pydantic import BaseConfig, BaseModel
from starlite import Body, RequestEncodingType, Starlite, UploadFile, post
class FormData(BaseModel):
cv: UploadFile
diploma: UploadFile
class Config(BaseConfig):
arbitrary_types_allowed = True
@post(path="/")
async def handle_file_upload(
data: FormData = Body(media_type=RequestEncodingType.MULTI_PART),
) -> dict:
cv_content = await data.cv.read()
diploma_content = await data.diploma.read()
return {"cv": cv_content.decode(), "diploma": diploma_content.decode()}
app = Starlite(route_handlers=[handle_file_upload])
Files as a dictionary#
If you do not care about parsing and validation and only want to access the form data as a dictionary, you can use a dict
instead:
from typing import Dict
from starlite import Body, RequestEncodingType, Starlite, UploadFile, post
@post(path="/")
async def handle_file_upload(
data: Dict[str, UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART),
) -> Dict[str, str]:
file_contents = {}
for name, file in data.items():
content = await file.read()
file_contents[name] = content.decode()
return file_contents
app = Starlite(route_handlers=[handle_file_upload])
from starlite import Body, RequestEncodingType, Starlite, UploadFile, post
@post(path="/")
async def handle_file_upload(
data: dict[str, UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART),
) -> dict[str, str]:
file_contents = {}
for name, file in data.items():
content = await file.read()
file_contents[name] = content.decode()
return file_contents
app = Starlite(route_handlers=[handle_file_upload])
Files as a list#
Finally, you can also access the files as a list without the filenames:
from typing import Dict, List
from starlite import Body, RequestEncodingType, Starlite, UploadFile, post
@post(path="/")
async def handle_file_upload(
data: List[UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART),
) -> Dict[str, str]:
file_contents = {}
for file in data:
content = await file.read()
file_contents[file.filename] = content.decode()
return file_contents
app = Starlite(route_handlers=[handle_file_upload])
from starlite import Body, RequestEncodingType, Starlite, UploadFile, post
@post(path="/")
async def handle_file_upload(
data: list[UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART),
) -> dict[str, str]:
file_contents = {}
for file in data:
content = await file.read()
file_contents[file.filename] = content.decode()
return file_contents
app = Starlite(route_handlers=[handle_file_upload])
MessagePack data#
To receive MessagePack data, specify the appropriate Content-Type
for Body
, by using RequestEncodingType.MESSAGEPACK
:
from typing import Dict
from starlite import Body, RequestEncodingType, Starlite, post
@post(path="/")
def msgpack_handler(data: Dict = Body(media_type=RequestEncodingType.MESSAGEPACK)) -> Dict:
# This will try to parse the request body as `MessagePack` regardless of the
# `Content-Type`
return data
app = Starlite(route_handlers=[msgpack_handler])
from starlite import Body, RequestEncodingType, Starlite, post
@post(path="/")
def msgpack_handler(data: dict = Body(media_type=RequestEncodingType.MESSAGEPACK)) -> dict:
# This will try to parse the request body as `MessagePack` regardless of the
# `Content-Type`
return data
app = Starlite(route_handlers=[msgpack_handler])