Requests
You can easily interact with incoming requests – as well as associated input, files and cookies – by leveraging intuitive methods, helpers and the powerful dependency injection system at the heart of Expanse.
Interacting with the request
Accessing the request
By default, the route functions or controller methods do not give access
to the incoming request, however you easily inject it by adding a type-hint for
the expanse.http.request.Request
class. It will be automatically be resolved via the service container:
# app/http/controller/user_controller.py
from expanse.http.request import Request
class UserController:
def list(request: Request) -> Response:
active = request.query("active")
# Retrieve the users
users = ...
...
Request data
URL
The url
property of the request object provides access to a URL
object that allows you to easily
retrieve information about the current request URL, like the full URL, its path or query parameters.
from expanse.http.request import Request
# Original URL: http://127.0.0.1:8001/users?active=true
request: Request
request.url.full # http://127.0.0.1:8001/users
request.url.hostname # 127.0.0.1
request.url.port # 8001
request.url.path # /users
request.url.query # active=true
Method
You can retrieve the HTTP method of the request by using the method
property of the request object.
from expanse.http.request import Request
request: Request
request.method # GET
Note that the method is always in uppercase.
Headers
You can retrieve the headers of the request by using the headers
property of the request object. The returned
objects behaves like a case-insensitive dictionary and you can access the headers by their name.
from expanse.http.request import Request
request: Request
assert request.headers["Content-Type"] == request.headers["content-type"]
Form data
Expanse provides a few ways to retrieve the request form data depending on the use cases.
Raw form data via dependency injection
The easiest way to have access to the raw form data is by type-hinting your route
with expanse.http.form.Form
and retrieve the value through the fields
attribute.
from expanse.common.http.form import Form
def create_user(form: Form) -> Response:
name = form.fields["username"].value
...
Note that every piece of data in this case will be a string and the validation of the data will need to be done manually. However, you can leverage the built-in validation mechanism instead.
Validated form data via dependency injection
Most of the time, you will want to validate the form data.
For that purpose, you can specify a validation model that will be used to validate the form data. You can
technically put your validation models anywhere but the recommended location for them
are app/http/request/models/forms/
.
# app/http/request/models/forms/create_user_form.py
from pydantic import BaseModel
class CreateUserFormModel(BaseModel):
username: str
age: int | None = None
Now that you have a validation model you can use it to validate the form data:
from expanse.common.http.form import Form
from app.http.request.models.forms.create_user_form import CreateUserFormModel
def create_user(form: Form[CreateUserFormModel]) -> Response:
if form.is_submitted() and form.is_valid():
name = form.data.username
age = form.data.age
...
data
is only available if there were no validation errors so, before accessing
it, be sure to check if the data is valid with the is_valid()
method.
JSON data
Expanse provides a few ways to retrieve the request form data depending on the use cases.
Raw JSON data using the request
The easiest way to have access to the raw form data is retrieving the request in your route handler
and access its form
attribute.
from expanse.http.request import Request
def create_user(request: Request) -> Response:
raw_data = request.form
...
Note that in this case it will be exactly what was sent to your application and any validation of the data will need to be done manually. However, you can leverage the built-in validation mechanism instead.
Validated JSON data via dependency injection
Most of the time, you will want to validate the JSON data.
For that purpose, you can specify a validation model that will be used to validate the JSON data. You can
technically put your validation models anywhere but the recommended location for them
are app/http/request/models/json/
.
# app/http/request/models/json/article.py
from pydantic import BaseModel
class ArticleData(BaseModel):
title: str
content: str
draft: bool = True
Now that you have a validation model you can use it to validate the JSON data:
from expanse.common.http.json import JSON
from app.http.request.models.json.article import ArticleData
def create_article(data: JSON[ArticleData]) -> Response:
# data is a validated instance of ArticleData
...
If the data sent is invalid, Expanse will automatically handle the validation error
and return a 422 Unprocessable Entity
HTTP error with a detailed JSON representation
of the error.
For instance, with the model defined above and the following request:
POST https://example.com/articles HTTP/1.1
Content-Type: application/json
{"content": "Content"}
Expanse would automatically send the following response:
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"code": "validation_error",
"detail": [
{
"loc": ["title"],
"message": "Field required",
"type": "missing",
}
]
}
Query parameters
Expanse provides a few ways to retrieve the query parameters depending on the use cases.
Raw query parameters using the request
The easiest way to have access to the raw query parameters is by retrieving the request in your route handler
and access its query_params
attribute.
from expanse.http.request import Request
def create_user(request: Request):
params = request.query_params
Request.params
is a dictionary-like object that allows you to access the query parameters by their name.
Validated query parameters via dependency injection
Most of the time, you will want to validate the query parameters.
For that purpose, you can specify a validation model that will be used to validate the query parameters. You can
technically put your validation models anywhere but the recommended location for them
are app/http/request/models/queries/
.
# app/http/request/models/queries/list_users.py
from pydantic import BaseModel
class ListUsersParameters(BaseModel):
active: bool | None = None
Now that you have a validation model you can use it to validate the query parameters:
from expanse.common.http.query import Query
from app.http.request.models.queries.list_users import ListUsersParameters
def create_article(q: Query[ListUsersParameters]) -> Response:
assert isinstance(q, ListUsersParameters)
if q.active:
# Retrieve active users
...
else:
# Retrieve all users
...
If the data sent is invalid, Expanse will automatically handle the validation error
and return a 422 Unprocessable Entity
HTTP error with a detailed JSON representation
of the error.