How to avoid the "Circular view path" exception with Spring MVC test
Solution 1:
@Controller
→ @RestController
I had the same issue and I noticed that my controller was also annotated with @Controller
. Replacing it with @RestController
solved the issue. Here is the explanation from Spring Web MVC:
@RestController is a composed annotation that is itself meta-annotated with @Controller and @ResponseBody indicating a controller whose every method inherits the type-level @ResponseBody annotation and therefore writes directly to the response body vs view resolution and rendering with an HTML template.
Solution 2:
I solved this problem by using @ResponseBody like below:
@RequestMapping(value = "/resturl", method = RequestMethod.GET, produces = {"application/json"})
@ResponseStatus(HttpStatus.OK)
@Transactional(value = "jpaTransactionManager")
public @ResponseBody List<DomainObject> findByResourceID(@PathParam("resourceID") String resourceID) {
Solution 3:
This has nothing to do with Spring MVC testing.
When you don't declare a ViewResolver
, Spring registers a default InternalResourceViewResolver
which creates instances of JstlView
for rendering the View
.
The JstlView
class extends InternalResourceView
which is
Wrapper for a JSP or other resource within the same web application. Exposes model objects as request attributes and forwards the request to the specified resource URL using a javax.servlet.RequestDispatcher.
A URL for this view is supposed to specify a resource within the web application, suitable for RequestDispatcher's forward or include method.
Emphasis mine. In other words, the view, before rendering, will try to get a RequestDispatcher
to which to forward()
. Before doing this it checks the following
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.)");
}
where path
is the view name, what you returned from the @Controller
. In this example, that is preference
. The variable uri
holds the uri of the request being handled, which is /context/preference
.
The code above realizes that if you were to forward to /context/preference
, the same servlet (since the same handled the previous) would handle the request and you would go into an endless loop.
When you declare a ThymeleafViewResolver
and a ServletContextTemplateResolver
with a specific prefix
and suffix
, it builds the View
differently, giving it a path like
WEB-INF/web-templates/preference.html
ThymeleafView
instances locate the file relative to the ServletContext
path by using a
ServletContextResourceResolver
templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);`
which eventually
return servletContext.getResourceAsStream(resourceName);
This gets a resource that is relative to the ServletContext
path. It can then use the TemplateEngine
to generate the HTML. There's no way an endless loop can happen here.