Response object
To manage HTTP responses, Rapidy uses the Response object.
For simple scenarios of retrieving or saving data, rapidy.http.Response may not be required, but if you want to flexibly manage the response of your HTTP handler, this section will explain how to do that.
The Response object in Rapidy is a wrapper around aiohttp.Response.
Unlike aiohttp.Response, rapidy.http.Response has become much more versatile, and it now has convenient data serialization.
More about aiohttp.Response can be found here.
Attributes
body
body: Any | None = None — the message body (can be almost any object).
To set the body, create a Response(body=...) object or use the body setter of an existing instance.
from rapidy.http import Response, get
@get('/')
async def handler() -> Response:
return Response(
body={'hello': 'rapidy'},
)
@get('/')
async def handler() -> ...:
response = Response()
response.body = {'hello': 'rapidy'}
...
@get('/')
async def handler(response: Response) -> ...:
response.body = {'hello': 'rapidy'}
...
To get the body, use the body getter of the Response object.
...
@get('/')
async def handler() -> ...:
response = Response(body={'hello': 'rapidy'})
body = response.body
...
The logic for preparing and transforming data is tied to the content_type attribute.
A bytes type object will be sent as the body without changes.
If you want Pydantic flags, such as exclude_none and others, to work, the passed object must be an instance of pydantic.BaseModel.
text
text: Any | None = None — the textual message body (can be almost any object).
To set the response body as text, create a Response(text=...) object or use the text setter of an existing instance.
from rapidy.http import Response, get
@get('/')
async def handler() -> Response:
return Response(
text = 'hello rapidy',
)
@get('/')
async def handler() -> ...:
response = Response()
response.text = 'hello rapidy'
...
@get('/')
async def handler(response: Response) -> ...:
response.text = 'hello rapidy'
...
To get the response body as text, use the text getter of the Response object.
...
@get('/')
async def handler() -> ...:
response = Response(text='hello rapidy')
body = response.text
...
The logic for preparing and transforming data is tied to the content_type attribute.
A str type object will be sent as the body without checks (content_type="text/plain").
If the data is not str, the same data transformation logic as for body applies.
content_type
content_type: str | ContentType | None = None — the attribute that allows you to manage the Content-Type header.
The Content-Type header informs the client (browser, API client, another server) about the type of data contained in the HTTP response body.
To set the content_type, create a Response(content_type=...) object or use the content_type setter of an existing instance.
from rapidy.http import get, Response, ContentType
@get('/')
async def handler() -> Response:
return Response(
body={'hello': 'rapidy!'},
content_type=ContentType.json,
# or
content_type='application/json',
)
@get('/')
async def handler() -> dict[str, str]:
response = Response()
response.content_type = ContentType.json
# or
response.content_type = 'application/json'
return {'hello': 'rapidy!'}
@get('/')
async def handler(response: Response) -> dict[str, str]:
response.content_type = ContentType.json
# or
response.content_type = 'application/json'
return {'hello': 'rapidy!'}
To get the content_type, use the content_type getter of the Response object.
...
@get('/')
async def handler() -> ...:
response = Response()
content_type = response.content_type
...
If content_type is specified, the provided data will be transformed accordingly.
If content_type is not specified, the content_type will be determined automatically based on the type of data the server is sending.
content_type="application/json
content_type="application/json" — the data is converted to JSON using jsonify(dumps=True)
and encoded according to charset.
from rapidy.http import get, Response, ContentType
@get('/')
async def handler() -> Response:
return Response(
body={'hello': 'rapidy!'},
content_type=ContentType.json,
) # {"hello": "rapidy!"}
If the passed object is a string Response(body="string"), the string, according to the JSON standard, will be double-escaped:
content_type="text/*
content_type="text/*" (any text type: text/plain, text/html, etc.) — if the data is of type str, it is sent without changes.
Otherwise, it is transformed into a string using jsonify(dumps=False).
from rapidy.http import get, Response, ContentType
@get('/')
async def handler() -> Response:
return Response(
text='hello rapidy!',
content_type=ContentType.text_any,
) # "hello rapidy!"
If after jsonify(dumps=False) the object is not a string, it is further transformed using json_encoder
to avoid double escaping.
content_type - any other MIME-type.
If the data is of type bytes, it is sent without changes.
Otherwise, it is transformed into a string using jsonify(dumps=True) and encoded according to charset.
If content_type is not specified, it is set automatically:
-
body: dict | BaseModel | dataclass→content_type="application/json" -
body: str | Enum | int | float | Decimal | bool→content_type="text/plain" -
body: Any→content_type="application/octet-stream"
status
status: int = 200 — HTTP response code.
from rapidy.http import Response, get
@get('/')
async def handler() -> Response:
return Response(
status=201,
)
set_status
To set the status, use the set_status method of the Response object.
...
@get('/')
async def handler() -> ...:
response = Response()
response.set_status(200)
...
@get('/')
async def handler(response: Response) -> ...:
response.set_status(200)
...
headers
headers: Mapping[str, str] | None = None — additional response headers.
from rapidy.http import Response, get
@get('/')
async def handler() -> Response:
return Response(
headers={'Some-Header': '123'},
)
To get the headers, use the headers getter of the Response object.
cookies
cookies: SimpleCookie | None = None — response cookies to be set in the browser.
To get the cookies, use the cookies getter of the Response object.
set_cookie
To set cookies, use the set_cookie method of the Response object.
from rapidy.http import Response, get
@get('/')
async def handler() -> ...:
response = Response()
response.set_cookie('SomeCookie', 'SomeValue')
...
@get('/')
async def handler(response: Response) -> ...:
response.set_cookie('SomeCookie', 'SomeValue')
...
del_cookie
To delete cookies, use the del_cookie method of the Response object.
...
@get('/')
async def handler() -> ...:
response = Response()
response.del_cookie('SomeCookie')
...
charset
charset: str | Charset | None = 'utf-8' — the encoding used for encoding and decoding data.
To set the charset, create a Response(charset=...) object or use the charset setter of an existing instance.
from rapidy.http import Response, get, Charset
@get('/')
async def handler() -> Response:
return Response(
charset=Charset.utf32,
# or
charset='utf32',
)
@get('/')
async def handler(response: Response) -> ...:
response.charset = Charset.utf32
# or
response.charset = 'utf32'
To get the charset, use the charset getter of the Response object.
last_modified
last_modified: int | float | datetime.datetime | str | None = None — the attribute responsible for managing the Last-Modified header.
To set last_modified, use the last_modified setter of the Response object.
import datetime
from rapidy.http import get, Response
@get('/')
async def handler() -> ...:
response = Response()
# or
response.last_modified = datetime.datetime(2024, 2, 24, 12, 0, 0, tzinfo=datetime.timezone.utc)
# or
response.last_modified = 'Wed, 21 Oct 2024 07:28:00 GMT'
...
@get('/')
async def handler(response: Response) -> ...:
# or
response.last_modified = datetime.datetime(2024, 2, 24, 12, 0, 0, tzinfo=datetime.timezone.utc)
# or
response.last_modified = 'Wed, 21 Oct 2024 07:28:00 GMT'
...
To get last_modified, use the last_modified getter of the Response object.
...
@get('/')
async def handler() -> ...:
response = Response()
last_modified = response.last_modified
...
Full example of building an HTTP handler using last_modified
from datetime import datetime, timezone
from typing import Annotated
from pydantic import BeforeValidator
from rapidy.http import Header, HTTPNotModified, Response, get
def parse_http_date(value: str) -> datetime:
return datetime.strptime(value, '%a, %d %b %Y %H:%M:%S GMT').replace(tzinfo=timezone.utc)
IFModifiedSince = Annotated[
datetime | None,
BeforeValidator(parse_http_date),
Header(None, alias='If-Modified-Since'),
]
@get('/')
async def handler(
response: Response,
if_modified_since: IFModifiedSince,
) -> str:
last_mod_time = datetime(2024, 2, 24, 12, 0, 0, tzinfo=timezone.utc)
# Check if the client sent the `If-Modified-Since` header
if if_modified_since and if_modified_since >= last_mod_time:
raise HTTPNotModified
response.last_modified = last_mod_time # Set the last modified date
return 'success'
etag
etag: ETag | str — the attribute responsible for managing the Etag header.
To set etag, use the etag setter of the Response object.
from rapidy.http import get, Response
@get('/')
async def handler() -> ...:
response = Response()
response.etag = '33a64df551425fcc55e4d42a148795d9f25f89d4'
...
@get('/')
async def handler(response: Response) -> ...:
response.etag = '33a64df551425fcc55e4d42a148795d9f25f89d4'
...
To get etag, use the etag getter of the Response object.
Full example of building an HTTP handler using etag.
import hashlib
from rapidy.http import Header, HTTPNotModified, Response, get
@get('/')
async def handler(
response: Response,
if_none_match: str | None = Header(None, alias='If-None-Match'),
) -> str:
content = 'success'
# Generate ETag based on content
etag_value = hashlib.md5(content.encode()).hexdigest()
# Check If-None-Match
if if_none_match and if_none_match == etag_value:
raise HTTPNotModified
response.etag = etag_value
return content
include
include: set[str] | dict[str, Any] | None = None — the include parameter from Pydantic, specifying which fields to include.
from pydantic import BaseModel, Field
from rapidy.http import Response, get
class Result(BaseModel):
value: str = Field('data', alias='someValue')
another_value: str = Field('another_data', alias='someAnotherValue')
@get('/')
async def handler() -> Response:
return Response(
Result(),
include={'value'},
) # {'someValue': 'data'}
exclude
exclude: set[str] | dict[str, Any] | None = None — the exclude parameter from Pydantic, specifying which fields to exclude.
from pydantic import BaseModel, Field
from rapidy.http import Response, get
class Result(BaseModel):
value: str = Field('data', alias='someValue')
another_value: str = Field('another_data', alias='someAnotherValue')
@get('/')
async def handler() -> Response:
return Response(
Result(),
exclude={'value'},
) # {'someAnotherValue': 'another_data'}
by_alias
by_alias: bool = True — the by_alias parameter from Pydantic, determining whether to use attribute aliases during serialization.
from pydantic import BaseModel, Field
from rapidy.http import get, Response
class Result(BaseModel):
value: str = Field('data', alias='someValue')
@get('/',)
async def handler() -> Response:
return Response(
Result(),
by_alias=True, # <-- default
) # {"someValue": "data"}
...
@get('/')
async def handler() -> Response:
return Response(
Result(),
by_alias=False,
) # {"value": "data"}
exclude_unset
exclude_unset: bool = False — the exclude_unset parameter from Pydantic, excluding fields not explicitly set to their default value.
from pydantic import BaseModel, Field
from rapidy.http import Response, get
class Result(BaseModel):
value: str = Field('data', alias='someValue')
another_value: str = Field('another_data', alias='someAnotherValue')
@get('/')
async def handler() -> Response:
return Response(
Result(someAnotherValue='new_data'),
exclude_unset=False, # <-- default
) # {"someValue": "data", "someAnotherValue": "new_data"}
...
@get('/')
async def handler() -> Response:
return Response(
Result(someAnotherValue='new_data'),
exclude_unset=True,
) # {"someAnotherValue": "new_data"}
exclude_defaults
exclude_defaults: bool = False — the exclude_defaults parameter from Pydantic, excluding fields with default values even if they were explicitly set.
from pydantic import BaseModel, Field
from rapidy.http import Response, get
class Result(BaseModel):
value: str = Field('data', alias='someValue')
@get('/')
async def handler() -> Response:
return Response(
Result(),
exclude_defaults=False, # <-- default
) # {"value": "data"}
...
@get('/')
async def handler() -> Response:
return Response(
Result(),
exclude_defaults=True,
) # {}
exclude_none
exclude_none: bool = False — the exclude_none parameter from Pydantic, excluding fields with None values from the output.
from pydantic import BaseModel, Field
from rapidy.http import Response, get
class Result(BaseModel):
value: str = Field('data', alias='someValue')
none_value: None = None
@get('/')
async def handler() -> Response:
return Response(
Result(),
exclude_none=False, # <-- default
) # {"someValue": "data", "none_value": null}
...
@get('/')
async def handler() -> Response:
return Response(
Result(),
exclude_none=True,
) # {"someValue": "data"}
custom_encoder
custom_encoder: Callable | None = None — the custom_encoder parameter from Pydantic, allowing you to specify a custom encoder.
json_encoder
json_encoder: Callable = json.dumps — a function that takes an object and returns its JSON representation.
from typing import Any
from rapidy.http import Response, get
def custom_encoder(obj: Any) -> str:
...
@get('/')
async def handler() -> Response:
return Response(
body={'hello': 'rapidy!'}, # will be converted to a string by Rapidy's internal tools
json_encoder=custom_encoder, # Converts the obtained string above into a JSON object using the `custom_encoder` function
)