1. ホーム
  2. spring

[解決済み] Spring MVCのテストで "Circular view path "例外を回避する方法

2022-06-06 18:04:08

質問

あるコントローラに以下のようなコードがあります。

@Controller
@RequestMapping("/preference")
public class PreferenceController {

    @RequestMapping(method = RequestMethod.GET, produces = "text/html")
    public String preference() {
        return "preference";
    }
}

を使ってテストしようとしているだけです。 Spring MVCテスト を以下のようにします。

@ContextConfiguration
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class PreferenceControllerTest {

    @Autowired
    private WebApplicationContext ctx;

    private MockMvc mockMvc;
    @Before
    public void setup() {
        mockMvc = webAppContextSetup(ctx).build();
    }

    @Test
    public void circularViewPathIssue() throws Exception {
        mockMvc.perform(get("/preference"))
               .andDo(print());
    }
}

以下のような例外が発生します。

循環ビューパス [環境設定]: 現在のハンドラ URL [/環境設定] に戻ってディスパッチされます。 ハンドラ URL [/preference]に再びディスパッチします。ViewResolverの設定を確認してください。(ヒント: これは、デフォルトのビュー名生成により、指定されていないビューの結果である可能性があります。 の名前生成のためです)。

私が不思議に思うのは を読み込むと正常に動作することです。 を読み込むと正常に動作します。

<bean class="org.thymeleaf.templateresolver.ServletContextTemplateResolver" id="webTemplateResolver">
    <property name="prefix" value="WEB-INF/web-templates/" />
    <property name="suffix" value=".html" />
    <property name="templateMode" value="HTML5" />
    <property name="characterEncoding" value="UTF-8" />
    <property name="order" value="2" />
    <property name="cacheable" value="false" />
</bean>

私は、アプリがこのテンプレートリゾルバを使用する際に、テンプレートリゾルバによって追加されたプレフィックスが、"循環ビューパス"がないことを保証することをよく承知しています。

しかし、それでは、Spring MVC テストを使用してどのように私のアプリをテストすればよいのでしょうか。

どのように解決するには?

これはSpring MVCのテストとは関係ありません。

を宣言しない場合 ViewResolver を宣言しなかった場合、Springはデフォルトの InternalResourceViewResolver のインスタンスを作成します。 JstlView をレンダリングするために View .

JstlView クラスは InternalResourceView であり、これは

同じWebアプリケーション内のJSPや他のリソースのラッパーです。 モデルオブジェクトをリクエストの属性として公開し、リクエストを javax.servlet.RequestDispatcherを使って指定されたリソースのURLへ転送します。

このビューの URL はウェブアプリケーション内のリソースを指定することになっています。 このビューの URL は、RequestDispatcher の forward または include メソッドに適した、ウェブアプリケーション内のリソースを指定することになっています。 メソッドに適したものです。

強調は私です。言い換えると、ビューはレンダリング前に RequestDispatcher に対して forward() . これを行う前に、次のようなチェックが行われます。

if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) {
    throw new ServletException("Circular view path [" + path + "]: would dispatch back " +
                        "to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " +
                        "(Hint: This may be the result of an unspecified view, due to default view name generation.)");
}

ここで path はビューの名前です。 @Controller . この例では、これは preference . 変数 uri は処理されるリクエストの uri を保持します。 /context/preference .

上のコードでは、もしあなたが /context/preference に転送した場合、同じサーブレットが (前のリクエストを処理したのと同じなので) リクエストを処理することになり、無限ループに入ることになることを上のコードは認識しています。


を宣言すると ThymeleafViewResolverServletContextTemplateResolver に特定の prefixsuffix を構築します。 View のようなパスを与えて、異なる方法で構築します。

WEB-INF/web-templates/preference.html

ThymeleafView インスタンスは、ファイルを ServletContext のパスに対して ServletContextResourceResolver

templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);`

というのは、最終的に

return servletContext.getResourceAsStream(resourceName);

これは ServletContext パスに相対的なリソースを取得します。これは TemplateEngine を使ってHTMLを生成します。ここで無限ループが起こるわけがないのです。