137

コントローラーの 1 つに次のコードがあります。

@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());
    }
}

次の例外が発生します。

Circular view path [preference]: 現在のハンドラー 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 テストを使用してアプリをテストするにはどうすればよいでしょうか?

4

22 に答える 22

108

以下のように @ResponseBody を使用して、この問題を解決しました。

@RequestMapping(value = "/resturl", method = RequestMethod.GET, produces = {"application/json"})
    @ResponseStatus(HttpStatus.OK)
    @Transactional(value = "jpaTransactionManager")
    public @ResponseBody List<DomainObject> findByResourceID(@PathParam("resourceID") String resourceID) {
于 2013-12-01T12:58:34.463 に答える
72

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

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

JstlViewクラスは次のように拡張InternalResourceViewされます

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

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

鉱山を強調します。言い換えると、ビューは、レンダリングする前に、RequestDispatcherto which toを取得しようとし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と、同じサーブレット (前のサーブレットが同じ処理を行ったため) が要求を処理し、無限ループに陥ることを認識しています。


特定のandで aThymeleafViewResolverと aを宣言すると、別の方法で が構築され、次のようなパスが与えられます。ServletContextTemplateResolverprefixsuffixView

WEB-INF/web-templates/preference.html

ThymeleafViewインスタンスはServletContextServletContextResourceResolver

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

最終的には

return servletContext.getResourceAsStream(resourceName);

これにより、パスに相対的なリソースが取得されServletContextます。その後、 を使用しTemplateEngineて HTML を生成できます。ここで無限ループが発生することはありません。

于 2013-09-15T17:03:51.187 に答える
41

これが私がこの問題を解決した方法です:

@Before
    public void setup() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/jsp/view/");
        viewResolver.setSuffix(".jsp");
 
        mockMvc = MockMvcBuilders.standaloneSetup(new HelpController())
                                 .setViewResolvers(viewResolver)
                                 .build();
    }

また、.x​​ml ファイルでこれの Bean を作成することもできます

<bean id = "viewResolver" class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/view/"/>
    <property name="suffix" value=".jsp"/>
</bean
于 2014-02-13T13:24:28.773 に答える
25

テストではなく、Spring Boot を使用して Web ページをロードしようとしていますが、この問題が発生しました。私の解決策は、わずかに異なる状況を考慮して、上記の解決策とは少し異なりました。(それらの答えは私が理解するのに役立ちましたが。)

Maven での Spring Boot スターターの依存関係を次のように変更するだけで済みました。

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
</dependency>

に:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

「web」を「thymeleaf」に変更するだけで問題が解決しました。

于 2018-01-05T13:40:26.893 に答える
16

ビューのレンダリングを実際に気にしない場合の簡単な修正方法を次に示します。

循環ビュー パスをチェックしない InternalResourceViewResolver のサブクラスを作成します。

public class StandaloneMvcTestViewResolver extends InternalResourceViewResolver {

    public StandaloneMvcTestViewResolver() {
        super();
    }

    @Override
    protected AbstractUrlBasedView buildView(final String viewName) throws Exception {
        final InternalResourceView view = (InternalResourceView) super.buildView(viewName);
        // prevent checking for circular view paths
        view.setPreventDispatchLoop(false);
        return view;
    }
}

次に、それを使用してテストをセットアップします。

MockMvc mockMvc;

@Before
public void setUp() {
    final MyController controller = new MyController();

    mockMvc =
            MockMvcBuilders.standaloneSetup(controller)
                    .setViewResolvers(new StandaloneMvcTestViewResolver())
                    .build();
}
于 2015-01-06T11:45:03.223 に答える
0

InternalResourceViewResolverアノテーションを使用してSpring Webアプリを構成します。構成にBeanを追加することで問題が解決しました。それが役立つことを願っています。

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.example.springmvc" })
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Bean
    public InternalResourceViewResolver internalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/jsp/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
}
于 2015-05-12T12:58:07.700 に答える
-2

別の簡単なアプローチ:

package org.yourpackagename;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

      @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(PreferenceController.class);
        }


    public static void main(String[] args) {
        SpringApplication.run(PreferenceController.class, args);
    }
}
于 2016-04-09T20:20:32.620 に答える