How to avoid the "Circular view path" exception with Spring MVC test
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) {
@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.
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.