Edited at

【SpringBoot】producesを設定したエンドポイントでHttpMediaTypeNotAcceptableExceptionになった場合の対処


原因

produces = "text/csv"が設定されているのにJSONを返そうとしたことが原因でした。


状況

状況は以下の通りです。


実現したかったもの


  • csvを返す(produces = "text/csv"を設定)エンドポイントを立てる

  • 集計エラー時はステータス400で、JSONにエラー内容を詰めて返す

実際には以下のようなアノテーションを付与していました。

@PostMapping(value = "[エンドポイント]", produces = "text/csv")


起きたこと


  • エラー時にステータスが406になる


    • エラー内容も空になる



  • サーバーサイドではHttpMediaTypeNotAcceptableExceptionが発生する


対策

producesの指定を無くせばOKでした。

@PostMapping(value = "[エンドポイント]")

producesを指定しない場合場合はよしなに扱われるようなので、問題が出なければ指定しなくてもよいと思われます。


補足

動的かつ明示的に指定する必要がある場合、HttpServletResponseを用いてレスポンスを直接書き込む方法が考えられます。

その場合は以下のようにして書けます。

@PostMapping(value = "/api/csv")

public void csv(HttpServletResponse response) {
String csv = // CSV取得処理;

response.setHeader(
"Content-Disposition",
"attachment;filename=\"" + /* ファイル名 */ + "\""
);
response.setCharacterEncoding("Shift-JIS");
response.setContentType("text/csv");

try (Writer writer = response.getWriter()) {
writer.write(csv);
} catch (IOException e) {
// エラー処理
}
}


感想

エラー処理はExceptionHandlerを用いて共通化していましたが、コントローラーで設定したproducesがそこまで貫通してくると思わず原因特定に時間がかかってしまいました。

HttpMediaTypeNotAcceptableExceptionとは書いてあったんですが……。


参考にさせていただいた記事