コントローラーの1つに次のコードがあります。
@Controller
@RequestMapping("/preference")
public class PreferenceController {
@RequestMapping(method = RequestMethod.GET, produces = "text/html")
public String preference() {
return "preference";
}
}
次のようにSpring MVC testを使用してテストしようとしています。
@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 [/設定]に再度ディスパッチします。 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は、RequestDispatcherのforwardメソッドまたはincludeメソッドに適したWebアプリケーション内のリソースを指定することになっています。
太字は私のものです。つまり、ビューは、レンダリングの前に、forward()
へのRequestDispatcher
を取得しようとします。これを行う前に、以下をチェックします
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
に転送する場合、同じサーブレット(前のものを処理したため)が要求を処理し、無限ループに入ることを認識しています。
特定のThymeleafViewResolver
およびServletContextTemplateResolver
を使用してprefix
およびsuffix
を宣言すると、View
のビルドが異なり、次のようなパスが与えられます。
WEB-INF/web-templates/preference.html
ThymeleafView
インスタンスは、ServletContext
を使用して、ServletContextResourceResolver
パスに関連するファイルを見つけます
templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);`
最終的に
return servletContext.getResourceAsStream(resourceName);
これは、ServletContext
パスに関連するリソースを取得します。その後、TemplateEngine
を使用してHTMLを生成できます。ここで無限ループが発生することはありません。
以下のように@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) {
@Controller
→@RestController
私は同じ問題を抱えていて、コントローラーにも@Controller
という注釈が付けられていることに気付きました。この問題を@RestController
に置き換えることで解決しました。 Spring Web MVC の説明を次に示します。
@RestControllerは、それ自体が@Controllerおよび@ResponseBodyでメタアノテーションされた合成アノテーションであり、すべてのメソッドがタイプレベルの@ResponseBodyアノテーションを継承するコントローラーを示すため、応答テンプレートとビューの解像度に直接書き込み、HTMLテンプレートでレンダリングします。
これは私がこの問題を解決した方法です:
@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();
}
ビューのレンダリングを実際に気にしない場合の簡単な修正方法を次に示します。
循環ビューパスをチェックしない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();
}
Spring Bootを使用している場合、thymleaf依存関係をpom.xmlに追加します。
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
私は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」に変更するだけで問題が解決しました。
ThymeleafでSpring Bootを使用しています。これは私のために働いたものです。 JSPについても同様の回答がありますが、JSPではなくHTMLを使用していることに注意してください。これらは、説明したように標準のSpring Bootプロジェクトのようなフォルダーsrc/main/resources/templates
にあります here 。これもあなたのケースかもしれません。
@InjectMocks
private MyController myController;
@Before
public void setup()
{
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(myController)
.setViewResolvers(viewResolver())
.build();
}
private ViewResolver viewResolver()
{
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("classpath:templates/");
viewResolver.setSuffix(".html");
return viewResolver;
}
お役に立てれば。
Thymeleafの場合:
Spring 4とthymeleafの使用を開始しましたが、このエラーが発生したとき、次のコードを追加することで解決しました。
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="order" value="0" />
</bean>
/
の後に/preference
を追加すると、問題が解決しました。
@Test
public void circularViewPathIssue() throws Exception {
mockMvc.perform(get("/preference/"))
.andDo(print());
}
@Controller
注釈を使用する場合は、@RequestMapping
および@ResponseBody
注釈が必要です。注釈を追加してからもう一度お試しください@ResponseBody
私の場合、Kotlin + Springブートを試していましたが、Circular View Pathの問題に遭遇しました。私がオンラインで得た提案はすべて、以下を試してみるまで助けられませんでした。
元々、@Controller
を使用してコントローラーに注釈を付けていました
import org.springframework.stereotype.Controller
次に、@Controller
を@RestController
に置き換えました
import org.springframework.web.bind.annotation.RestController
そしてそれは働いた。
compile( "org.springframework.boot:spring-boot-starter-thymeleaf")依存関係をgradleファイルに追加してみてください。Thymeleafはビューのマッピングに役立ちます。
これは、Springが「設定」を削除し、「設定」を再度追加して、リクエストUriと同じパスを作成しているために発生しています。
次のように動作します:Uriをリクエスト: "/ preference"
「設定」を削除:「/」
パスを追加: "/" + "preference"
終了文字列: "/ preference"
これは、Springが例外をスローすることで通知するループに入ります。
「preferenceView」などの別のビュー名を付けるのがあなたの興味の中で一番です。
このアノテーションを使用して、Spring Webアプリを構成します。この問題は、InternalResourceViewResolver
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;
}
}