/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.servlet.view; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.http.MediaType; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.mock.web.test.MockServletContext; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.FixedContentNegotiationStrategy; import org.springframework.web.accept.HeaderContentNegotiationStrategy; import org.springframework.web.accept.MappingMediaTypeFileExtensionResolver; import org.springframework.web.accept.ParameterContentNegotiationStrategy; import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; /** * @author Arjen Poutsma */ public class ContentNegotiatingViewResolverTests { private ContentNegotiatingViewResolver viewResolver; private MockHttpServletRequest request; @Before public void createViewResolver() { StaticWebApplicationContext wac = new StaticWebApplicationContext(); wac.setServletContext(new MockServletContext()); wac.refresh(); viewResolver = new ContentNegotiatingViewResolver(); viewResolver.setApplicationContext(wac); request = new MockHttpServletRequest("GET", "/test"); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); } @After public void resetRequestContextHolder() { RequestContextHolder.resetRequestAttributes(); } @Test public void getMediaTypeAcceptHeaderWithProduces() throws Exception { Set<MediaType> producibleTypes = Collections.singleton(MediaType.APPLICATION_XHTML_XML); request.setAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, producibleTypes); request.addHeader("Accept", "text/html,application/xml;q=0.9,application/xhtml+xml,*/*;q=0.8"); viewResolver.afterPropertiesSet(); List<MediaType> result = viewResolver.getMediaTypes(request); assertEquals("Invalid content type", new MediaType("application", "xhtml+xml"), result.get(0)); } @Test public void resolveViewNameWithPathExtension() throws Exception { request.setRequestURI("/test.xls"); ViewResolver viewResolverMock = mock(ViewResolver.class); viewResolver.setViewResolvers(Collections.singletonList(viewResolverMock)); viewResolver.afterPropertiesSet(); View viewMock = mock(View.class, "application_xls"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock.resolveViewName(viewName, locale)).willReturn(null); given(viewResolverMock.resolveViewName(viewName + ".xls", locale)).willReturn(viewMock); given(viewMock.getContentType()).willReturn("application/vnd.ms-excel"); View result = viewResolver.resolveViewName(viewName, locale); assertSame("Invalid view", viewMock, result); } @Test public void resolveViewNameWithAcceptHeader() throws Exception { request.addHeader("Accept", "application/vnd.ms-excel"); Map<String, MediaType> mapping = Collections.singletonMap("xls", MediaType.valueOf("application/vnd.ms-excel")); MappingMediaTypeFileExtensionResolver extensionsResolver = new MappingMediaTypeFileExtensionResolver(mapping); ContentNegotiationManager manager = new ContentNegotiationManager(new HeaderContentNegotiationStrategy()); manager.addFileExtensionResolvers(extensionsResolver); viewResolver.setContentNegotiationManager(manager); ViewResolver viewResolverMock = mock(ViewResolver.class); viewResolver.setViewResolvers(Collections.singletonList(viewResolverMock)); View viewMock = mock(View.class, "application_xls"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock.resolveViewName(viewName, locale)).willReturn(null); given(viewResolverMock.resolveViewName(viewName + ".xls", locale)).willReturn(viewMock); given(viewMock.getContentType()).willReturn("application/vnd.ms-excel"); View result = viewResolver.resolveViewName(viewName, locale); assertSame("Invalid view", viewMock, result); } @Test public void resolveViewNameWithInvalidAcceptHeader() throws Exception { request.addHeader("Accept", "application"); viewResolver.afterPropertiesSet(); View result = viewResolver.resolveViewName("test", Locale.ENGLISH); assertNull(result); } @Test public void resolveViewNameWithRequestParameter() throws Exception { request.addParameter("format", "xls"); Map<String, MediaType> mapping = Collections.singletonMap("xls", MediaType.valueOf("application/vnd.ms-excel")); ParameterContentNegotiationStrategy paramStrategy = new ParameterContentNegotiationStrategy(mapping); viewResolver.setContentNegotiationManager(new ContentNegotiationManager(paramStrategy)); ViewResolver viewResolverMock = mock(ViewResolver.class); viewResolver.setViewResolvers(Collections.singletonList(viewResolverMock)); viewResolver.afterPropertiesSet(); View viewMock = mock(View.class, "application_xls"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock.resolveViewName(viewName, locale)).willReturn(null); given(viewResolverMock.resolveViewName(viewName + ".xls", locale)).willReturn(viewMock); given(viewMock.getContentType()).willReturn("application/vnd.ms-excel"); View result = viewResolver.resolveViewName(viewName, locale); assertSame("Invalid view", viewMock, result); } @Test public void resolveViewNameWithDefaultContentType() throws Exception { request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); MediaType mediaType = new MediaType("application", "xml"); FixedContentNegotiationStrategy fixedStrategy = new FixedContentNegotiationStrategy(mediaType); viewResolver.setContentNegotiationManager(new ContentNegotiationManager(fixedStrategy)); ViewResolver viewResolverMock1 = mock(ViewResolver.class, "viewResolver1"); ViewResolver viewResolverMock2 = mock(ViewResolver.class, "viewResolver2"); viewResolver.setViewResolvers(Arrays.asList(viewResolverMock1, viewResolverMock2)); viewResolver.afterPropertiesSet(); View viewMock1 = mock(View.class, "application_xml"); View viewMock2 = mock(View.class, "text_html"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock1.resolveViewName(viewName, locale)).willReturn(viewMock1); given(viewResolverMock2.resolveViewName(viewName, locale)).willReturn(viewMock2); given(viewMock1.getContentType()).willReturn("application/xml"); given(viewMock2.getContentType()).willReturn("text/html;charset=ISO-8859-1"); View result = viewResolver.resolveViewName(viewName, locale); assertSame("Invalid view", viewMock1, result); } @Test public void resolveViewNameAcceptHeader() throws Exception { request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); ViewResolver viewResolverMock1 = mock(ViewResolver.class); ViewResolver viewResolverMock2 = mock(ViewResolver.class); viewResolver.setViewResolvers(Arrays.asList(viewResolverMock1, viewResolverMock2)); viewResolver.afterPropertiesSet(); View viewMock1 = mock(View.class, "application_xml"); View viewMock2 = mock(View.class, "text_html"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock1.resolveViewName(viewName, locale)).willReturn(viewMock1); given(viewResolverMock2.resolveViewName(viewName, locale)).willReturn(viewMock2); given(viewMock1.getContentType()).willReturn("application/xml"); given(viewMock2.getContentType()).willReturn("text/html;charset=ISO-8859-1"); View result = viewResolver.resolveViewName(viewName, locale); assertSame("Invalid view", viewMock2, result); } // SPR-9160 @Test public void resolveViewNameAcceptHeaderSortByQuality() throws Exception { request.addHeader("Accept", "text/plain;q=0.5, application/json"); viewResolver.setContentNegotiationManager(new ContentNegotiationManager(new HeaderContentNegotiationStrategy())); ViewResolver htmlViewResolver = mock(ViewResolver.class); ViewResolver jsonViewResolver = mock(ViewResolver.class); viewResolver.setViewResolvers(Arrays.asList(htmlViewResolver, jsonViewResolver)); View htmlView = mock(View.class, "text_html"); View jsonViewMock = mock(View.class, "application_json"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(htmlViewResolver.resolveViewName(viewName, locale)).willReturn(htmlView); given(jsonViewResolver.resolveViewName(viewName, locale)).willReturn(jsonViewMock); given(htmlView.getContentType()).willReturn("text/html"); given(jsonViewMock.getContentType()).willReturn("application/json"); View result = viewResolver.resolveViewName(viewName, locale); assertSame("Invalid view", jsonViewMock, result); } // SPR-9807 @Test public void resolveViewNameAcceptHeaderWithSuffix() throws Exception { request.addHeader("Accept", "application/vnd.example-v2+xml"); ViewResolver viewResolverMock = mock(ViewResolver.class); viewResolver.setViewResolvers(Arrays.asList(viewResolverMock)); viewResolver.afterPropertiesSet(); View viewMock = mock(View.class, "application_xml"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock.resolveViewName(viewName, locale)).willReturn(viewMock); given(viewMock.getContentType()).willReturn("application/*+xml"); View result = viewResolver.resolveViewName(viewName, locale); assertSame("Invalid view", viewMock, result); assertEquals(new MediaType("application", "vnd.example-v2+xml"), request.getAttribute(View.SELECTED_CONTENT_TYPE)); } @Test public void resolveViewNameAcceptHeaderDefaultView() throws Exception { request.addHeader("Accept", "application/json"); ViewResolver viewResolverMock1 = mock(ViewResolver.class); ViewResolver viewResolverMock2 = mock(ViewResolver.class); viewResolver.setViewResolvers(Arrays.asList(viewResolverMock1, viewResolverMock2)); View viewMock1 = mock(View.class, "application_xml"); View viewMock2 = mock(View.class, "text_html"); View viewMock3 = mock(View.class, "application_json"); List<View> defaultViews = new ArrayList<>(); defaultViews.add(viewMock3); viewResolver.setDefaultViews(defaultViews); viewResolver.afterPropertiesSet(); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock1.resolveViewName(viewName, locale)).willReturn(viewMock1); given(viewResolverMock2.resolveViewName(viewName, locale)).willReturn(viewMock2); given(viewMock1.getContentType()).willReturn("application/xml"); given(viewMock2.getContentType()).willReturn("text/html;charset=ISO-8859-1"); given(viewMock3.getContentType()).willReturn("application/json"); View result = viewResolver.resolveViewName(viewName, locale); assertSame("Invalid view", viewMock3, result); } @Test public void resolveViewNameFilename() throws Exception { request.setRequestURI("/test.html"); ViewResolver viewResolverMock1 = mock(ViewResolver.class, "viewResolver1"); ViewResolver viewResolverMock2 = mock(ViewResolver.class, "viewResolver2"); viewResolver.setViewResolvers(Arrays.asList(viewResolverMock1, viewResolverMock2)); viewResolver.afterPropertiesSet(); View viewMock1 = mock(View.class, "application_xml"); View viewMock2 = mock(View.class, "text_html"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock1.resolveViewName(viewName, locale)).willReturn(viewMock1); given(viewResolverMock1.resolveViewName(viewName + ".html", locale)).willReturn(null); given(viewResolverMock2.resolveViewName(viewName, locale)).willReturn(null); given(viewResolverMock2.resolveViewName(viewName + ".html", locale)).willReturn(viewMock2); given(viewMock1.getContentType()).willReturn("application/xml"); given(viewMock2.getContentType()).willReturn("text/html;charset=ISO-8859-1"); View result = viewResolver.resolveViewName(viewName, locale); assertSame("Invalid view", viewMock2, result); } @Test public void resolveViewNameFilenameDefaultView() throws Exception { request.setRequestURI("/test.json"); Map<String, MediaType> mapping = Collections.singletonMap("json", MediaType.APPLICATION_JSON); PathExtensionContentNegotiationStrategy pathStrategy = new PathExtensionContentNegotiationStrategy(mapping); viewResolver.setContentNegotiationManager(new ContentNegotiationManager(pathStrategy)); ViewResolver viewResolverMock1 = mock(ViewResolver.class); ViewResolver viewResolverMock2 = mock(ViewResolver.class); viewResolver.setViewResolvers(Arrays.asList(viewResolverMock1, viewResolverMock2)); View viewMock1 = mock(View.class, "application_xml"); View viewMock2 = mock(View.class, "text_html"); View viewMock3 = mock(View.class, "application_json"); List<View> defaultViews = new ArrayList<>(); defaultViews.add(viewMock3); viewResolver.setDefaultViews(defaultViews); viewResolver.afterPropertiesSet(); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock1.resolveViewName(viewName, locale)).willReturn(viewMock1); given(viewResolverMock1.resolveViewName(viewName + ".json", locale)).willReturn(null); given(viewResolverMock2.resolveViewName(viewName, locale)).willReturn(viewMock2); given(viewResolverMock2.resolveViewName(viewName + ".json", locale)).willReturn(null); given(viewMock1.getContentType()).willReturn("application/xml"); given(viewMock2.getContentType()).willReturn("text/html;charset=ISO-8859-1"); given(viewMock3.getContentType()).willReturn("application/json"); View result = viewResolver.resolveViewName(viewName, locale); assertSame("Invalid view", viewMock3, result); } @Test public void resolveViewContentTypeNull() throws Exception { request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); ViewResolver viewResolverMock = mock(ViewResolver.class); viewResolver.setViewResolvers(Collections.singletonList(viewResolverMock)); viewResolver.afterPropertiesSet(); View viewMock = mock(View.class, "application_xml"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock.resolveViewName(viewName, locale)).willReturn(viewMock); given(viewMock.getContentType()).willReturn(null); View result = viewResolver.resolveViewName(viewName, locale); assertNull("Invalid view", result); } @Test public void resolveViewNameRedirectView() throws Exception { request.addHeader("Accept", "application/json"); request.setRequestURI("/test"); StaticWebApplicationContext webAppContext = new StaticWebApplicationContext(); webAppContext.setServletContext(new MockServletContext()); webAppContext.refresh(); UrlBasedViewResolver urlViewResolver = new InternalResourceViewResolver(); urlViewResolver.setApplicationContext(webAppContext); ViewResolver xmlViewResolver = mock(ViewResolver.class); viewResolver.setViewResolvers(Arrays.<ViewResolver>asList(xmlViewResolver, urlViewResolver)); View xmlView = mock(View.class, "application_xml"); View jsonView = mock(View.class, "application_json"); viewResolver.setDefaultViews(Arrays.asList(jsonView)); viewResolver.afterPropertiesSet(); String viewName = "redirect:anotherTest"; Locale locale = Locale.ENGLISH; given(xmlViewResolver.resolveViewName(viewName, locale)).willReturn(xmlView); given(jsonView.getContentType()).willReturn("application/json"); View actualView = viewResolver.resolveViewName(viewName, locale); assertEquals("Invalid view", RedirectView.class, actualView.getClass()); } @Test public void resolveViewNoMatch() throws Exception { request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9"); ViewResolver viewResolverMock = mock(ViewResolver.class); viewResolver.setViewResolvers(Collections.singletonList(viewResolverMock)); viewResolver.afterPropertiesSet(); View viewMock = mock(View.class, "application_xml"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock.resolveViewName(viewName, locale)).willReturn(viewMock); given(viewMock.getContentType()).willReturn("application/pdf"); View result = viewResolver.resolveViewName(viewName, locale); assertNull("Invalid view", result); } @Test public void resolveViewNoMatchUseUnacceptableStatus() throws Exception { viewResolver.setUseNotAcceptableStatusCode(true); request.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9"); ViewResolver viewResolverMock = mock(ViewResolver.class); viewResolver.setViewResolvers(Collections.singletonList(viewResolverMock)); viewResolver.afterPropertiesSet(); View viewMock = mock(View.class, "application_xml"); String viewName = "view"; Locale locale = Locale.ENGLISH; given(viewResolverMock.resolveViewName(viewName, locale)).willReturn(viewMock); given(viewMock.getContentType()).willReturn("application/pdf"); View result = viewResolver.resolveViewName(viewName, locale); assertNotNull("Invalid view", result); MockHttpServletResponse response = new MockHttpServletResponse(); result.render(null, request, response); assertEquals("Invalid status code set", 406, response.getStatus()); } @Test public void nestedViewResolverIsNotSpringBean() throws Exception { StaticWebApplicationContext webAppContext = new StaticWebApplicationContext(); webAppContext.setServletContext(new MockServletContext()); webAppContext.refresh(); InternalResourceViewResolver nestedResolver = new InternalResourceViewResolver(); nestedResolver.setApplicationContext(webAppContext); nestedResolver.setViewClass(InternalResourceView.class); viewResolver.setViewResolvers(new ArrayList<>(Arrays.asList(nestedResolver))); FixedContentNegotiationStrategy fixedStrategy = new FixedContentNegotiationStrategy(MediaType.TEXT_HTML); viewResolver.setContentNegotiationManager(new ContentNegotiationManager(fixedStrategy)); viewResolver.afterPropertiesSet(); String viewName = "view"; Locale locale = Locale.ENGLISH; View result = viewResolver.resolveViewName(viewName, locale); assertNotNull("Invalid view", result); } }