diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md
index 88ca4a00..ab0b3530 100644
--- a/docs/en/docs/release-notes.md
+++ b/docs/en/docs/release-notes.md
@@ -22,6 +22,7 @@ accessible via `from esmerald import Controller`.
- Fix escaped " in TemplateResponse.
- Fix TemplateResponse's auto-detection of the media-type when used directly.
- Don't mangle strings by default for other media-types than json.
+- Don't mangle returned responses.
## 3.6.2
diff --git a/esmerald/routing/base.py b/esmerald/routing/base.py
index c5da8874..70ba00b1 100644
--- a/esmerald/routing/base.py
+++ b/esmerald/routing/base.py
@@ -275,7 +275,9 @@ def _get_response_container_handler(
cookies: ResponseCookies,
headers: Dict[str, Any],
media_type: str,
- ) -> Callable[[ResponseContainer, Type[Esmerald], Dict[str, Any]], LilyaResponse]:
+ ) -> Callable[
+ [Union[ResponseContainer, LilyaResponse], Type[Esmerald], Dict[str, Any]], LilyaResponse
+ ]:
"""
Creates a handler for ResponseContainer types.
@@ -290,22 +292,30 @@ def _get_response_container_handler(
"""
async def response_content(
- data: ResponseContainer, app: Type["Esmerald"], **kwargs: Dict[str, Any]
+ data: Union[ResponseContainer, LilyaResponse],
+ app: Type["Esmerald"],
+ **kwargs: Dict[str, Any],
) -> LilyaResponse:
_headers = {**self.get_headers(headers), **data.headers}
_cookies = self.get_cookies(data.cookies, cookies)
- response: Response = data.to_response(
- app=app,
- headers=_headers,
- status_code=data.status_code or self.status_code,
- media_type=media_type,
- )
+ if isinstance(data, LilyaResponse):
+ response: LilyaResponse = data
+ else:
+ response = data.to_response(
+ app=app,
+ headers=_headers,
+ status_code=data.status_code or self.status_code,
+ media_type=media_type,
+ )
for cookie in _cookies:
response.set_cookie(**cookie)
return response
return cast(
- Callable[[ResponseContainer, Type["Esmerald"], Dict[str, Any]], LilyaResponse],
+ Callable[
+ [Union[ResponseContainer, LilyaResponse], Type["Esmerald"], Dict[str, Any]],
+ LilyaResponse,
+ ],
response_content,
)
@@ -435,7 +445,7 @@ def _get_default_handler(
async def response_content(data: Any, **kwargs: Dict[str, Any]) -> LilyaResponse:
data = await self.get_response_data(data=data)
_cookies = self.get_cookies(cookies)
- if isinstance(data, JSONResponse):
+ if isinstance(data, LilyaResponse):
response = data
response.status_code = self.status_code
response.background = self.background
diff --git a/esmerald/routing/router.py b/esmerald/routing/router.py
index 1d30bcb8..fb205991 100644
--- a/esmerald/routing/router.py
+++ b/esmerald/routing/router.py
@@ -481,9 +481,9 @@ async def another(request: Request) -> str:
path = "/"
else:
assert path.startswith("/"), "A path prefix must start with '/'"
- assert not path.endswith(
- "/"
- ), "A path must not end with '/', as the routes will start with '/'"
+ assert not path.endswith("/"), (
+ "A path must not end with '/', as the routes will start with '/'"
+ )
new_routes: list[Any] = []
for route in routes or []:
@@ -511,9 +511,9 @@ async def another(request: Request) -> str:
)
new_routes.append(route)
- assert lifespan is None or (
- on_startup is None and on_shutdown is None
- ), "Use either 'lifespan' or 'on_startup'/'on_shutdown', not both."
+ assert lifespan is None or (on_startup is None and on_shutdown is None), (
+ "Use either 'lifespan' or 'on_startup'/'on_shutdown', not both."
+ )
super().__init__(
redirect_slashes=redirect_slashes,
diff --git a/tests/routing/test_routing_data.py b/tests/routing/test_routing_data.py
new file mode 100644
index 00000000..b18fcef0
--- /dev/null
+++ b/tests/routing/test_routing_data.py
@@ -0,0 +1,26 @@
+from typing import Optional
+
+from pydantic import BaseModel
+
+from esmerald import Esmerald, Form, Request
+from esmerald.routing.gateways import Gateway
+from esmerald.routing.handlers import route
+from esmerald.testclient import EsmeraldTestClient
+
+
+class Model(BaseModel):
+ id: str
+
+
+def test_get_and_post():
+ @route(methods=["GET", "POST"])
+ async def start(request: Request, data: Optional[Model] = Form()) -> bytes:
+ return b"hello world"
+
+ app = Esmerald(
+ debug=True,
+ routes=[Gateway("/", handler=start)],
+ )
+ client = EsmeraldTestClient(app)
+ response = client.get("/")
+ assert response.status_code == 200
diff --git a/tests/test_direct_responses.py b/tests/test_direct_responses.py
new file mode 100644
index 00000000..5e2ca25e
--- /dev/null
+++ b/tests/test_direct_responses.py
@@ -0,0 +1,51 @@
+import os
+
+from esmerald import Esmerald, Redirect, Request, Template
+from esmerald.config.template import TemplateConfig
+from esmerald.responses.base import RedirectResponse
+from esmerald.routing.gateways import Gateway
+from esmerald.routing.handlers import get
+from esmerald.testclient import EsmeraldTestClient
+
+
+def test_return_response_container(template_dir):
+ path = os.path.join(template_dir, "start.html")
+ with open(path, "w") as file:
+ file.write("Hello, world")
+
+ @get()
+ async def start(request: Request) -> Template:
+ return Redirect(path="/home", status_code=301)
+
+ app = Esmerald(
+ debug=True,
+ routes=[Gateway("/", handler=start)],
+ template_config=TemplateConfig(
+ directory=template_dir,
+ ),
+ middleware=[],
+ )
+ client = EsmeraldTestClient(app)
+ response = client.get("/", follow_redirects=False)
+ assert response.status_code == 301
+
+
+def test_return_response(template_dir):
+ path = os.path.join(template_dir, "start.html")
+ with open(path, "w") as file:
+ file.write("Hello, world")
+
+ @get()
+ async def start(request: Request) -> Template:
+ return RedirectResponse(url="/home", status_code=301)
+
+ app = Esmerald(
+ debug=True,
+ routes=[Gateway("/", handler=start)],
+ template_config=TemplateConfig(
+ directory=template_dir,
+ ),
+ )
+ client = EsmeraldTestClient(app)
+ response = client.get("/", follow_redirects=False)
+ assert response.status_code == 301