Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

馃悰Fix RuntimeError raised when HTTPException has a status code with no content #5365

Merged
merged 8 commits into from Sep 11, 2022
16 changes: 8 additions & 8 deletions fastapi/exception_handlers.py
@@ -1,19 +1,19 @@
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.utils import is_body_allowed_for_status_code
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.responses import JSONResponse, Response
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY


async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
async def http_exception_handler(request: Request, exc: HTTPException) -> Response:
headers = getattr(exc, "headers", None)
if headers:
return JSONResponse(
{"detail": exc.detail}, status_code=exc.status_code, headers=headers
)
else:
return JSONResponse({"detail": exc.detail}, status_code=exc.status_code)
if not is_body_allowed_for_status_code(exc.status_code):
return Response(status_code=exc.status_code, headers=headers)
return JSONResponse(
{"detail": exc.detail}, status_code=exc.status_code, headers=headers
)


async def request_validation_exception_handler(
Expand Down
46 changes: 46 additions & 0 deletions tests/test_starlette_exception.py
Expand Up @@ -18,6 +18,16 @@ async def read_item(item_id: str):
return {"item": items[item_id]}


@app.get("/http-no-body-statuscode-exception")
async def no_body_status_code_exception():
raise HTTPException(status_code=204)


@app.get("/http-no-body-statuscode-with-detail-exception")
async def no_body_status_code_with_detail_exception():
raise HTTPException(status_code=204, detail="I should just disappear!")


@app.get("/starlette-items/{item_id}")
async def read_starlette_item(item_id: str):
if item_id not in items:
Expand All @@ -31,6 +41,30 @@ async def read_starlette_item(item_id: str):
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/http-no-body-statuscode-exception": {
"get": {
"operationId": "no_body_status_code_exception_http_no_body_statuscode_exception_get",
"responses": {
"200": {
"content": {"application/json": {"schema": {}}},
"description": "Successful " "Response",
}
},
"summary": "No Body " "Status " "Code " "Exception",
}
},
"/http-no-body-statuscode-with-detail-exception": {
"get": {
"operationId": "no_body_status_code_with_detail_exception_http_no_body_statuscode_with_detail_exception_get",
"responses": {
"200": {
"content": {"application/json": {"schema": {}}},
"description": "Successful " "Response",
}
},
"summary": "No Body Status Code With Detail Exception",
}
},
"/items/{item_id}": {
"get": {
"responses": {
Expand Down Expand Up @@ -154,3 +188,15 @@ def test_get_starlette_item_not_found():
assert response.status_code == 404, response.text
assert response.headers.get("x-error") is None
assert response.json() == {"detail": "Item not found"}


def test_no_body_status_code_exception_handlers():
response = client.get("/http-no-body-statuscode-exception")
assert response.status_code == 204
assert not response.content


def test_no_body_status_code_with_detail_exception_handlers():
response = client.get("/http-no-body-statuscode-with-detail-exception")
assert response.status_code == 204
assert not response.content