```python
from starlette.applications import Starlette
+from starlette.exceptions import HTTPException
+from starlette.requests import Request
from starlette.responses import HTMLResponse
HTML_500_PAGE = ...
-async def not_found(request, exc):
+async def not_found(request: Request, exc: HTTPException):
return HTMLResponse(content=HTML_404_PAGE, status_code=exc.status_code)
-async def server_error(request, exc):
+async def server_error(request: Request, exc: HTTPException):
return HTMLResponse(content=HTML_500_PAGE, status_code=exc.status_code)
is handled. For example, to use JSON style responses:
```python
-async def http_exception(request, exc):
+async def http_exception(request: Request, exc: HTTPException):
return JSONResponse({"detail": exc.detail}, status_code=exc.status_code)
exception_handlers = {
}
```
+The `HTTPException` is also equipped with the `headers` argument. Which allows the propagation
+of the headers to the response class:
+
+```python
+async def http_exception(request: Request, exc: HTTPException):
+ return JSONResponse(
+ {"detail": exc.detail},
+ status_code=exc.status_code,
+ headers=exc.headers
+ )
+```
+
## Errors and handled exceptions
It is important to differentiate between handled exceptions and errors.
handled exceptions. The `ExceptionMiddleware` implementation defaults to
returning plain-text HTTP responses for any `HTTPException`.
-* `HTTPException(status_code, detail=None)`
+* `HTTPException(status_code, detail=None, headers=None)`
You should only raise `HTTPException` inside routing or endpoints. Middleware
classes should instead just return appropriate responses directly.
class HTTPException(Exception):
- def __init__(self, status_code: int, detail: str = None) -> None:
+ def __init__(
+ self, status_code: int, detail: str = None, headers: dict = None
+ ) -> None:
if detail is None:
detail = http.HTTPStatus(status_code).phrase
self.status_code = status_code
self.detail = detail
+ self.headers = headers
def __repr__(self) -> str:
class_name = self.__class__.__name__
def http_exception(self, request: Request, exc: HTTPException) -> Response:
if exc.status_code in {204, 304}:
- return Response(status_code=exc.status_code)
- return PlainTextResponse(exc.detail, status_code=exc.status_code)
+ return Response(status_code=exc.status_code, headers=exc.headers)
+ return PlainTextResponse(
+ exc.detail, status_code=exc.status_code, headers=exc.headers
+ )
raise HTTPException(status_code=304)
+def with_headers(request):
+ raise HTTPException(status_code=200, headers={"x-potato": "always"})
+
+
class HandledExcAfterResponse:
async def __call__(self, scope, receive, send):
response = PlainTextResponse("OK", status_code=200)
Route("/not_acceptable", endpoint=not_acceptable),
Route("/no_content", endpoint=no_content),
Route("/not_modified", endpoint=not_modified),
+ Route("/with_headers", endpoint=with_headers),
Route("/handled_exc_after_response", endpoint=HandledExcAfterResponse()),
WebSocketRoute("/runtime_error", endpoint=raise_runtime_error),
]
assert response.text == ""
+def test_with_headers(client):
+ response = client.get("/with_headers")
+ assert response.status_code == 200
+ assert response.headers["x-potato"] == "always"
+
+
def test_websockets_should_raise(client):
with pytest.raises(RuntimeError):
with client.websocket_connect("/runtime_error"):