RFC 9110では、特に削除操作(DELETEリクエスト)の場合、レスポンスボディが含まれる場合は200 OK
を使用し、レスポンスボディがない場合は204 No Content
を使用すべきとされています。
a DELETE method is successfully applied, the origin server SHOULD send
a 202 (Accepted) status code if the action will likely succeed but has not yet been enacted,
a 204 (No Content) status code if the action has been enacted and no further information is to be supplied, or
a 200 (OK) status code if the action has been enacted and the response message includes a representation describing the status.
しかし、FastAPIはリクエストが成功した時は、デフォルトで200 OKステータスコードを返すようになっているので、今回は削除が成功した時に204 No Content
を返せるようにしてみます。
Response class - FastAPI
DELETEエンドポイントの設定
reports = {
"1": {"title": "Report 1", "description": "This is a report"},
"2": {"title": "Report 2", "description": "This is another report"},
"3": {"title": "Report 3", "description": "This is a third report"}
}
@app.get("/reports")
async def get_reports():
return reports
@app.delete("/reports/{report_id}", status_code=204)
async def delete_report(report_id: str):
if report_id in reports:
del reports[report_id]
else:
raise HTTPException(status_code=404, detail="Item not found")
エンドポイントの設定時に、status_code=204
を指定し、DELETE
が出来ていることを確認するため、GET
も用意しています。
Swagger UIで確認
status code 204
が返ってきました。
実装を確認する
Starlette
class Response:
media_type = None
charset = "utf-8"
def __init__(
self,
content: typing.Any = None,
status_code: int = 200,
headers: typing.Optional[typing.Mapping[str, str]] = None,
media_type: typing.Optional[str] = None,
background: typing.Optional[BackgroundTask] = None,
) -> None:
self.status_code = status_code
(略)
FastAPI
from starlette.responses import JSONResponse, Response
async def app(request: Request) -> Response:
try:
body: Any = None
if body_field:
if is_body_form:
body = await request.form()
stack = request.scope.get("fastapi_astack")
assert isinstance(stack, AsyncExitStack)
stack.push_async_callback(body.close)
else:
body_bytes = await request.body()
if body_bytes:
json_body: Any = Undefined
content_type_value = request.headers.get("content-type")
if not content_type_value:
json_body = await request.json()
else:
message = email.message.Message()
message["content-type"] = content_type_value
if message.get_content_maintype() == "application":
subtype = message.get_content_subtype()
if subtype == "json" or subtype.endswith("+json"):
json_body = await request.json()
if json_body != Undefined:
body = json_body
else:
body = body_bytes
def api_route(
self,
path: str,
*,
response_model: Any = Default(None),
status_code: Optional[int] = None,
(略)
),
) -> Callable[[DecoratedCallable], DecoratedCallable]:
def decorator(func: DecoratedCallable) -> DecoratedCallable:
self.add_api_route(
path,
func,
response_model=response_model,
status_code=status_code,
def delete(
self,
path: str,
*,
response_model: Any = Default(None),
status_code: Optional[int] = None,
(略)
FastAPIは、StarletteからResponseクラスをインポートして、Starletteが提供するレスポンスの生成と管理機能にアクセスし、
async def app(request: Request) -> Response
、def delete(...)
で、HTTPリクエストを受け取り、StarletteのResponse型を戻り値として設定して、DELETEレスポンスを返しているようです。
https://github.com/kei-kmj/redirect_slashes_test/pull/1/commits/947c1438d734f48569bd08b2f610c4a68d8a4466
参考