Open5

@ResponseStatusで、ステータスコード204(`NO_CONTENT`)を指定するとレスポンスボディが空になる->204の場合はボディは空にすべき

ふじしろふじしろ

元々以下のようなdeleteメソッドを実装していた。

@DeleteMapping("/{id}")
public DTO delete(@PathVariable("id") Long id) {
	return DTO.of(Service.delete(id));
}

このメソッドの役目は、指定されたIDのデータを物理削除して、削除に成功したデータの内容を返すこと。
レスポンスステータスコードは200が返される。

しかし、ステータスコードを204にすることになったので、以下のように記述を修正。
期待する動作は、

  • レスポンスステータスコードが204
  • レスポンスボディに削除に成功したデータが格納されている
@DeleteMapping("/{id}")
@ResponseStatus(code = HttpStatus.NO_CONTENT)
public DTO delete(@PathVariable("id") Long id) {
	return DTO.of(Service.delete(id));
}

この状態で試しに処理を実行したところ、
ステータスコードは204になったが、削除に成功したデータが格納されなくなってしまった

色々調べてみたところ、「204を返すときは、レスポンスボディは空であるべき」 ということだった。

成功したレスポンスは、もしレスポンスがステータスで表しているエンティティを含んでいるなら 200 (OK)、もし動作がまだ行われていないなら 202 (Accepted)、もし動作は行われたが、レスポンスにエンティティを含んでいないなら 204 (No Content) であるべきである。

DELETE メソッドが成功裡に適用された場合、 生成元サーバは、 動作状況に応じて,次のいずれかの状態°コードを送信するべきである:
動作は成功する見込みが高いが、 まだ実行済みでない場合 :202 (Accepted)
動作は実行済みで、 更なる情報は給されない場合 :204 (No Content)
動作は実行済みで、 応答メッセージが[ その状態°を述べる表現 ]を内包する場合 :200 (OK)

そして今回の現象は、おそらくSpringBootがこの内容に従ってよしなにしてくれているものと思われる(要検証)

今回はレスポンスで204が返されることが指定されているため、戻り値を削除した上で、対応する。

ふじしろふじしろ

RFCの仕様を見てみる(RFC7231)
https://datatracker.ietf.org/doc/html/rfc7231#section-6.3.5

(原文)
A 204 response is terminated by the first empty line after the header fields because it cannot contain a message body.
(DeepL)
204応答は、メッセージボディを含むことができないので、ヘッダーフィールド の後の最初の空行で終了する。

仕様によれば、「含むことができないit cannot contain...)」とされている。
なので204のボディは空にすべき、という仕様になっている模様。

https://shinkufencer.hateblo.jp/entry/2019/05/17/000000

ふじしろふじしろ

↑で貼り付けたRFC7231は既に廃止済みでRFC9110になっていた。
https://datatracker.ietf.org/doc/html/rfc9110

(原文)
A 204 response is terminated by the end of the header section; it cannot contain content or trailers.
(DeepL)
204応答はヘッダーセクションの終わりで終了する;コンテンツやトレーラーは含まれない。

内容はほぼ一緒。意味合いは同じと思われる。
RFC9110でこれまでボディと呼んでいたものが、コンテンツと呼称されるようになった模様。