1. 概要

この短い記事では、Spring例外「HttpMessageNotWritableException:プリセットされたContent-Typeを持つ[class…]のコンバーターがありません」を詳しく見ていきます。

まず、例外の背後にある主な原因に光を当てます。 次に、うさぎの穴を掘り下げて、実際の例を使用してそれを再現する方法を確認し、最後に、それを解決する方法を確認します。

2. 原因

詳細を深く掘り下げる前に、例外の意味を理解してみましょう。

例外のスタックトレースはすべてを示しています。これは、SpringがJavaオブジェクトをHTTP応答[に変換できる適切な HttpMessageConverterを見つけられなかったことを示しています。 X201X]。

基本的に、Springは“ Accept” ヘッダーに依存して、応答する必要のあるメディアタイプを検出します。

したがって、事前登録されたメッセージコンバーターのないメディアタイプを使用すると、例外でSpringが失敗します。

3. 例外の再現

Springが例外をスローする原因がわかったので、実際の例を使用してそれを再現する方法を見てみましょう。

ハンドラーメソッドを作成し、 HttpMessageConverter が登録されていないメディアタイプ(応答用)を指定するふりをしてみましょう。

たとえば、 APPLICATION_XML_VALUEまたは“ application / xml”を使用してみましょう。

@GetMapping(value = "/student/v3/{id}", produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<Student> getV3(@PathVariable("id") int id) {
    return ResponseEntity.ok(new Student(id, "Robert", "Miller", "BB"));
}

次に、 http:// localhost:8080 / api / student / v3 / 1 にリクエストを送信して、何が起こるかを見てみましょう。

curl http://localhost:8080/api/student/v3/1

エンドポイントは次の応答を返します。

{"timestamp":"2022-02-01T18:23:37.490+00:00","status":500,"error":"Internal Server Error","path":"/api/student/v3/1"}

実際、ログを見ると、SpringはHttpMessageNotWritableException例外で失敗します。

[org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class com.baeldung.boot.noconverterfound.model.Student] with preset Content-Type 'null']

したがって、 XMLとの間でStudentオブジェクトをマーシャリングおよびアンマーシャリングできるHttpMessageConverterがないため、例外がスローされます。

最後に、テストケースを作成して、Springが指定されたメッセージでHttpMessageNotWritableExceptionをスローすることを確認しましょう。

@Test
public void whenConverterNotFound_thenThrowException() throws Exception {
    String url = "/api/student/v3/1";

    this.mockMvc.perform(get(url))
      .andExpect(status().isInternalServerError())
      .andExpect(result -> assertThat(result.getResolvedException()).isInstanceOf(HttpMessageNotWritableException.class))
      .andExpect(result -> assertThat(result.getResolvedException()
        .getMessage()).contains("No converter for [class com.baeldung.boot.noconverterfound.model.Student] with preset Content-Type"));
}

4. ソリューション

例外を修正する唯一の方法は、メッセージコンバータが登録されているメディアタイプを使用することです。

Spring Bootは、自動構成に依存して組み込みメッセージコンバーターを登録します。

たとえば、Jackson 2の依存関係がクラスパスに存在する場合、MappingJackson2HttpMessageConverterを自動的に登録します。

そうは言っても、Spring Bootの WebスターターにJacksonが含まれていることを知って、APPLICATION_JSON_VALUEメディアタイプで新しいエンドポイントを作成しましょう。

@GetMapping(value = "/student/v2/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Student> getV2(@PathVariable("id") int id) {
    return ResponseEntity.ok(new Student(id, "Kevin", "Cruyff", "AA"));
}

それでは、テストケースを作成して、すべてが例外として機能することを確認しましょう。

@Test
public void whenJsonConverterIsFound_thenReturnResponse() throws Exception {
    String url = "/api/student/v2/1";

    this.mockMvc.perform(get(url))
      .andExpect(status().isOk())
      .andExpect(content().json("{'id':1,'firstName':'Kevin','lastName':'Cruyff', 'grade':'AA'}"));
}

ご覧のとおり、Springは MappingJackson2HttpMessageConverter のおかげで、 HttpMessageNotWritableException をスローしません。これは、内部でStudentオブジェクトのJSONへの変換を処理します。

5. 結論

この短いチュートリアルでは、Springが「HttpMessageNotWritableExceptionプリセットされたContent-Typeを持つ[class…]のコンバーターがありません」をスローする原因について詳しく説明しました。

その過程で、例外を作成する方法と実際にそれを修正する方法を紹介しました。

いつものように、例の完全なソースコードは、GitHubから入手できます。