diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.java
index 6c4b466214500a73c4db1e741b5184242c2efc65..c27eee3497d61d4991f1898d03a31965288f4369 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolver.java
@@ -37,9 +37,10 @@ import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
+import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
-import org.springframework.http.MediaType;
+import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
@@ -73,7 +74,11 @@ import org.springframework.web.util.WebUtils;
* {@code text/html} content type (based on the {@code html} file extension). A request for {@code /view} with a {@code
* text/html} request {@code Accept} header has the same result.
*
+ *
Additionally, this view resolver exposes the {@link #setDefaultViews(List) defaultViews} property, allowing you to
+ * override the views provided by the view resolvers.
+ *
* @author Arjen Poutsma
+ * @author Jeremy Grelle
* @see ViewResolver
* @see InternalResourceViewResolver
* @see BeanNameViewResolver
@@ -94,6 +99,8 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
private ConcurrentMap mediaTypes = new ConcurrentHashMap();
+ private List defaultViews;
+
private List viewResolvers;
public void setOrder(int order) {
@@ -131,6 +138,13 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
}
}
+ /**
+ * Sets the default views to use when a more specific view can not be obtained from the {@link ViewResolver} chain.
+ */
+ public void setDefaultViews(List defaultViews) {
+ this.defaultViews = defaultViews;
+ }
+
/**
* Sets the view resolvers to be wrapped by this view resolver.
*
@@ -235,16 +249,23 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
Collections.sort(requestedMediaTypes);
SortedMap views = new TreeMap();
+ List candidateViews = new ArrayList();
for (ViewResolver viewResolver : viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
- MediaType viewMediaType = MediaType.parseMediaType(view.getContentType());
- for (MediaType requestedMediaType : requestedMediaTypes) {
- if (requestedMediaType.includes(viewMediaType)) {
- if (!views.containsKey(requestedMediaType)) {
- views.put(requestedMediaType, view);
- break;
- }
+ candidateViews.add(view);
+ }
+ }
+ if (!CollectionUtils.isEmpty(defaultViews)) {
+ candidateViews.addAll(defaultViews);
+ }
+ for (View candidateView : candidateViews) {
+ MediaType viewMediaType = MediaType.parseMediaType(candidateView.getContentType());
+ for (MediaType requestedMediaType : requestedMediaTypes) {
+ if (requestedMediaType.includes(viewMediaType)) {
+ if (!views.containsKey(requestedMediaType)) {
+ views.put(requestedMediaType, candidateView);
+ break;
}
}
}
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolverTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolverTests.java
index 3fa76d36d60c211de8bd8b2839618d55526e34d1..d78c0a8e0faea037bd7849eb2aecdbc97a8b5a7d 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolverTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ContentNegotiatingViewResolverTests.java
@@ -18,23 +18,26 @@ package org.springframework.web.servlet.view;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import static org.easymock.EasyMock.*;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
+import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
-/** @author Arjen Poutsma */
+/**
+ * @author Arjen Poutsma
+ */
public class ContentNegotiatingViewResolverTests {
private ContentNegotiatingViewResolver viewResolver;
@@ -109,7 +112,45 @@ public class ContentNegotiatingViewResolverTests {
verify(viewResolverMock1, viewResolverMock2, viewMock1, viewMock2);
}
-
+
+ @Test
+ public void resolveViewNameAcceptHeaderDefaultView() throws Exception {
+ MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test");
+ request.addHeader("Accept", "application/json");
+ RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
+
+ ViewResolver viewResolverMock1 = createMock(ViewResolver.class);
+ ViewResolver viewResolverMock2 = createMock(ViewResolver.class);
+ List viewResolverMocks = new ArrayList();
+ viewResolverMocks.add(viewResolverMock1);
+ viewResolverMocks.add(viewResolverMock2);
+ viewResolver.setViewResolvers(viewResolverMocks);
+
+ View viewMock1 = createMock("application_xml", View.class);
+ View viewMock2 = createMock("text_html", View.class);
+ View viewMock3 = createMock("application_json", View.class);
+
+ List defaultViews = new ArrayList();
+ defaultViews.add(viewMock3);
+ viewResolver.setDefaultViews(defaultViews);
+
+ String viewName = "view";
+ Locale locale = Locale.ENGLISH;
+
+ expect(viewResolverMock1.resolveViewName(viewName, locale)).andReturn(viewMock1);
+ expect(viewResolverMock2.resolveViewName(viewName, locale)).andReturn(viewMock2);
+ expect(viewMock1.getContentType()).andReturn("application/xml");
+ expect(viewMock2.getContentType()).andReturn("text/html;charset=ISO-8859-1");
+ expect(viewMock3.getContentType()).andReturn("application/json");
+
+ replay(viewResolverMock1, viewResolverMock2, viewMock1, viewMock2, viewMock3);
+
+ View result = viewResolver.resolveViewName(viewName, locale);
+ assertSame("Invalid view", viewMock3, result);
+
+ verify(viewResolverMock1, viewResolverMock2, viewMock1, viewMock2, viewMock3);
+ }
+
@Test
public void resolveViewNameFilename() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test.html");
@@ -141,4 +182,45 @@ public class ContentNegotiatingViewResolverTests {
verify(viewResolverMock1, viewResolverMock2, viewMock1, viewMock2);
}
+ @Test
+ public void resolveViewNameFilenameDefaultView() throws Exception {
+ MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test.json");
+ RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
+
+ Map mediaTypes = new HashMap();
+ mediaTypes.put("json", "application/json");
+ viewResolver.setMediaTypes(mediaTypes);
+
+ ViewResolver viewResolverMock1 = createMock(ViewResolver.class);
+ ViewResolver viewResolverMock2 = createMock(ViewResolver.class);
+ List viewResolverMocks = new ArrayList();
+ viewResolverMocks.add(viewResolverMock1);
+ viewResolverMocks.add(viewResolverMock2);
+ viewResolver.setViewResolvers(viewResolverMocks);
+
+ View viewMock1 = createMock("application_xml", View.class);
+ View viewMock2 = createMock("text_html", View.class);
+ View viewMock3 = createMock("application_json", View.class);
+
+ List defaultViews = new ArrayList();
+ defaultViews.add(viewMock3);
+ viewResolver.setDefaultViews(defaultViews);
+
+ String viewName = "view";
+ Locale locale = Locale.ENGLISH;
+
+ expect(viewResolverMock1.resolveViewName(viewName, locale)).andReturn(viewMock1);
+ expect(viewResolverMock2.resolveViewName(viewName, locale)).andReturn(viewMock2);
+ expect(viewMock1.getContentType()).andReturn("application/xml");
+ expect(viewMock2.getContentType()).andReturn("text/html;charset=ISO-8859-1");
+ expect(viewMock3.getContentType()).andReturn("application/json");
+
+ replay(viewResolverMock1, viewResolverMock2, viewMock1, viewMock2, viewMock3);
+
+ View result = viewResolver.resolveViewName(viewName, locale);
+ assertSame("Invalid view", viewMock3, result);
+
+ verify(viewResolverMock1, viewResolverMock2, viewMock1, viewMock2, viewMock3);
+ }
+
}