プログラミング

実装・エラー解決

SpringのThymeleafでjavax.servlet.ServletExceptionが発生したしょうもない理由

この記事に書いてあること

Spring MVCで書いたコードの試験をしていた際、

javax.servlet.ServletException: Circular view path [sample]: 
would dispatch back to the current handler URL [/sample] again. Check your ViewResolver setup!

が発生したのですが、その理由がしょうもなかったので備忘メモ。

前提

  • Sprinv MVC5.x
  • Thymeleaf
  • Java11(あんまり関係ない)
  • Junit4(あまり関係ない)
  • ExceptionHandlerを使用

結論

実行用サンプル

同じURLへのforwardによる循環、ThymeleafのHTML欠落、@ResponseBodyで本文を返す場合を分けて再現します。 検証コードを見る。 JDK 17とMaven 3.9系で試せます。ソース・テスト・pom.xml・READMEをZIPに収録しています。

ViewController.java の全文

package info.syachiku.maintenance;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
class ViewController {
    @GetMapping("/external-view")
    String external() {
        return "home";
    }

    @GetMapping("/missing-view")
    String missing() {
        return "not-present";
    }

    @GetMapping("/body")
    @ResponseBody
    String body() {
        return "home";
    }
}

Spring Boot 3.5.16の検証では、InternalResourceViewResolver で /sample が sample を返すと、同じURLにforwardしようとして Circular view path になります。Thymeleafで not-present が見つからない場合は、TemplateInputException が原因に出ます。@ResponseBody は文字列を本文へ書くのでビューを探しません。旧 javax.servlet と現行 jakarta.servlet の名前を区別します。

旧環境では画面名とテンプレートの設定を見直して解決しました。調べるときは、同じURLへのforwardと、ThymeleafでHTMLが見つからない場合を分けます。

本論

SpringMVCとExceptionHandlerを使って、Controllerから特定の例外をスローしてHandler側で画面を返しています。

JUNIT4を使って試験をしているときに、試験ケースとして「エラーが発生した場合○○画面を返す」という試験を実施した際、想定通りExceptionHandlerに処理が委譲されているのですが、画面名を返したところで下記のエラー。

javax.servlet.ServletException: Circular view path [sample]: would dispatch back to the current handler URL [/sample] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
at org.springframework.web.servlet.view.InternalResourceView.prepareForRendering(InternalResourceView.java:210)
at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:148)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:317)
~~~~

旧記録の環境では、ExceptionHandlerが返す画面に対応するHTMLを用意していませんでした。ただし Circular view path は、ViewResolverが現在のURLへforwardしようとしたことを示すメッセージです。

まず例外を返したViewResolverをスタックトレースで確認します。InternalResourceViewなら現在URLとビュー名を見比べ、Thymeleafならテンプレートの配置とprefix・suffixを確認します。

-実装・エラー解決
-,