FastAPI 에서의 No Content (204) 응답 시 꼭 해줘야 하는 것

FastAPI 에서의 No Content (204) 응답 시 꼭 해줘야 하는 것

204 No Content

204 No Content - HTTP | MDN
The HTTP 204 No Content success status response code indicates that a request has succeeded, but that the client doesn’t need to navigate away from its current page.
개발할 땐 MDN이 매우 좋은 이정표가 될 것.

RFC 7230 문서에 의하면, 204 응답의 경우 Content-Length 를 헤더로 갖고있을 수 없도록 되어있다. (https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2) 하지만 FastAPI에서는 쉽게 실수함으로써 표준을 지키지 않음과 동시에 인프라 레이어에서 거슬리는 상황이 생길 수 있다.

FastAPI 에서의 204 처리


@app.post('/return-none/', status_code=204, response_model=DeleteItemResponse)
async def delete_item():
    pass

어플리케이션에서 막는 reponse_model 지정

이런 식으로 코드를 사용하였을 때, 서버를 구동시킬 수 없다. AssertionError: Status code 204 must not have a response body 라는 에러를 발생하면서.

여기까진 좋다. 하지만, 개발자들은 새로운 문제를 일으킬 수 있다.

@app.post('/return-none/', status_code=204)
async def return_none():
    pass


@app.post('/return-none-explicit/', status_code=204)
async def return_none_explicit():
    return None

이런 식으로 None을 return 하도록 하게 되면, FastAPI, Starlette 는 아래와 같은 response 를 주고 있다.

{'date': 'Wed, 22 Dec 2021 18:22:27 GMT', 'server': 'uvicorn', 'content-length': '4', 'content-type': 'application/json'}

datadog같은 APM에서 이런 warning 로그를 확인할 수 있을 것이다.

2021/12/24 07:53:43 [warn] 5196#5196: *1047308 upstream sent more data than specified in "Content-Length" header while reading upstream, client: 119.205.223.214, server: <🔗>, request: "POST <🔗> HTTP/1.1", upstream: "http://10.0.0.39:80/<🔗>", host: "<🔗>"

문제 해결

from fastapi import Response

@app.post('/return-http-response/', status_code=204)
async def return_http_response():
    return Response(status_code=204)

이렇게 Response 객체를 명시적으로 리턴해주면 문제 없다. content-lengthcontent-type 이 없는 response headers를 확인할 수 있을 것이다.

{'date': 'Wed, 22 Dec 2021 18:21:31 GMT', 'server': 'uvicorn'}

이를 해결하려고 하는 PR은 FastAPI 와 Starlette 에 모두 존재한다. 하지만, 머지가 되려면 갈 길이 여러모로 멀어보인다.