Support for parsed PathPatterns in Spring MVC

See gh-24945
This commit is contained in:
Rossen Stoyanchev
2020-06-15 11:20:32 +01:00
parent a0f4d81db7
commit 5225a57411
82 changed files with 5324 additions and 3011 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -16,9 +16,9 @@
package org.springframework.web.servlet.config.annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -34,16 +34,20 @@ import org.springframework.util.PathMatcher;
import org.springframework.validation.DefaultMessageCodesResolver;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
import org.springframework.web.testfixture.servlet.MockServletContext;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@@ -82,15 +86,16 @@ public class DelegatingWebMvcConfigurationTests {
@Captor
private ArgumentCaptor<List<HandlerExceptionResolver>> exceptionResolvers;
private final DelegatingWebMvcConfiguration delegatingConfig = new DelegatingWebMvcConfiguration();
private final DelegatingWebMvcConfiguration webMvcConfig = new DelegatingWebMvcConfiguration();
@Test
public void requestMappingHandlerAdapter() throws Exception {
delegatingConfig.setConfigurers(Collections.singletonList(webMvcConfigurer));
RequestMappingHandlerAdapter adapter = this.delegatingConfig.requestMappingHandlerAdapter(
this.delegatingConfig.mvcContentNegotiationManager(), this.delegatingConfig.mvcConversionService(),
this.delegatingConfig.mvcValidator());
public void requestMappingHandlerAdapter() {
webMvcConfig.setConfigurers(Collections.singletonList(webMvcConfigurer));
RequestMappingHandlerAdapter adapter = this.webMvcConfig.requestMappingHandlerAdapter(
this.webMvcConfig.mvcContentNegotiationManager(),
this.webMvcConfig.mvcConversionService(),
this.webMvcConfig.mvcValidator());
ConfigurableWebBindingInitializer initializer =
(ConfigurableWebBindingInitializer) adapter.getWebBindingInitializer();
@@ -114,10 +119,9 @@ public class DelegatingWebMvcConfigurationTests {
@Test
public void configureMessageConverters() {
final HttpMessageConverter<?> customConverter = mock(HttpMessageConverter.class);
final StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
List<WebMvcConfigurer> configurers = new ArrayList<>();
configurers.add(new WebMvcConfigurer() {
HttpMessageConverter<?> customConverter = mock(HttpMessageConverter.class);
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
WebMvcConfigurer configurer = new WebMvcConfigurer() {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(stringConverter);
@@ -127,13 +131,15 @@ public class DelegatingWebMvcConfigurationTests {
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, customConverter);
}
});
delegatingConfig.setConfigurers(configurers);
};
webMvcConfig.setConfigurers(Collections.singletonList(configurer));
RequestMappingHandlerAdapter adapter = delegatingConfig.requestMappingHandlerAdapter(
this.delegatingConfig.mvcContentNegotiationManager(), this.delegatingConfig.mvcConversionService(),
this.delegatingConfig.mvcValidator());
assertThat(adapter.getMessageConverters().size()).as("Only one custom converter should be registered").isEqualTo(2);
RequestMappingHandlerAdapter adapter = webMvcConfig.requestMappingHandlerAdapter(
this.webMvcConfig.mvcContentNegotiationManager(),
this.webMvcConfig.mvcConversionService(),
this.webMvcConfig.mvcValidator());
assertThat(adapter.getMessageConverters().size()).as("One custom converter expected").isEqualTo(2);
assertThat(adapter.getMessageConverters().get(0)).isSameAs(customConverter);
assertThat(adapter.getMessageConverters().get(1)).isSameAs(stringConverter);
}
@@ -142,8 +148,8 @@ public class DelegatingWebMvcConfigurationTests {
public void getCustomValidator() {
given(webMvcConfigurer.getValidator()).willReturn(new LocalValidatorFactoryBean());
delegatingConfig.setConfigurers(Collections.singletonList(webMvcConfigurer));
delegatingConfig.mvcValidator();
webMvcConfig.setConfigurers(Collections.singletonList(webMvcConfigurer));
webMvcConfig.mvcValidator();
verify(webMvcConfigurer).getValidator();
}
@@ -152,16 +158,16 @@ public class DelegatingWebMvcConfigurationTests {
public void getCustomMessageCodesResolver() {
given(webMvcConfigurer.getMessageCodesResolver()).willReturn(new DefaultMessageCodesResolver());
delegatingConfig.setConfigurers(Collections.singletonList(webMvcConfigurer));
delegatingConfig.getMessageCodesResolver();
webMvcConfig.setConfigurers(Collections.singletonList(webMvcConfigurer));
webMvcConfig.getMessageCodesResolver();
verify(webMvcConfigurer).getMessageCodesResolver();
}
@Test
public void handlerExceptionResolver() throws Exception {
delegatingConfig.setConfigurers(Collections.singletonList(webMvcConfigurer));
delegatingConfig.handlerExceptionResolver(delegatingConfig.mvcContentNegotiationManager());
public void handlerExceptionResolver() {
webMvcConfig.setConfigurers(Collections.singletonList(webMvcConfigurer));
webMvcConfig.handlerExceptionResolver(webMvcConfig.mvcContentNegotiationManager());
verify(webMvcConfigurer).configureMessageConverters(converters.capture());
verify(webMvcConfigurer).configureContentNegotiation(contentNegotiationConfigurer.capture());
@@ -178,29 +184,30 @@ public class DelegatingWebMvcConfigurationTests {
}
@Test
public void configureExceptionResolvers() throws Exception {
List<WebMvcConfigurer> configurers = new ArrayList<>();
configurers.add(new WebMvcConfigurer() {
public void configureExceptionResolvers() {
WebMvcConfigurer configurer = new WebMvcConfigurer() {
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(new DefaultHandlerExceptionResolver());
}
});
delegatingConfig.setConfigurers(configurers);
};
webMvcConfig.setConfigurers(Collections.singletonList(configurer));
HandlerExceptionResolverComposite composite =
(HandlerExceptionResolverComposite) delegatingConfig
.handlerExceptionResolver(delegatingConfig.mvcContentNegotiationManager());
assertThat(composite.getExceptionResolvers().size()).as("Only one custom converter is expected").isEqualTo(1);
(HandlerExceptionResolverComposite) webMvcConfig
.handlerExceptionResolver(webMvcConfig.mvcContentNegotiationManager());
assertThat(composite.getExceptionResolvers().size())
.as("Only one custom converter is expected")
.isEqualTo(1);
}
@Test
public void configurePathMatch() throws Exception {
final PathMatcher pathMatcher = mock(PathMatcher.class);
final UrlPathHelper pathHelper = mock(UrlPathHelper.class);
public void configurePathMatcher() {
PathMatcher pathMatcher = mock(PathMatcher.class);
UrlPathHelper pathHelper = mock(UrlPathHelper.class);
List<WebMvcConfigurer> configurers = new ArrayList<>();
configurers.add(new WebMvcConfigurer() {
WebMvcConfigurer configurer = new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseRegisteredSuffixPatternMatch(true)
@@ -208,18 +215,122 @@ public class DelegatingWebMvcConfigurationTests {
.setUrlPathHelper(pathHelper)
.setPathMatcher(pathMatcher);
}
});
delegatingConfig.setConfigurers(configurers);
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/");
}
};
RequestMappingHandlerMapping handlerMapping = delegatingConfig.requestMappingHandlerMapping(
delegatingConfig.mvcContentNegotiationManager(), delegatingConfig.mvcConversionService(),
delegatingConfig.mvcResourceUrlProvider());
assertThat(handlerMapping).isNotNull();
assertThat(handlerMapping.useRegisteredSuffixPatternMatch()).as("PathMatchConfigurer should configure RegisteredSuffixPatternMatch").isEqualTo(true);
assertThat(handlerMapping.useSuffixPatternMatch()).as("PathMatchConfigurer should configure SuffixPatternMatch").isEqualTo(true);
assertThat(handlerMapping.useTrailingSlashMatch()).as("PathMatchConfigurer should configure TrailingSlashMatch").isEqualTo(false);
assertThat(handlerMapping.getUrlPathHelper()).as("PathMatchConfigurer should configure UrlPathHelper").isEqualTo(pathHelper);
assertThat(handlerMapping.getPathMatcher()).as("PathMatchConfigurer should configure PathMatcher").isEqualTo(pathMatcher);
MockServletContext servletContext = new MockServletContext();
webMvcConfig.setConfigurers(Collections.singletonList(configurer));
webMvcConfig.setServletContext(servletContext);
webMvcConfig.setApplicationContext(new GenericWebApplicationContext(servletContext));
BiConsumer<UrlPathHelper, PathMatcher> configAssertion = (helper, matcher) -> {
assertThat(helper).isSameAs(pathHelper);
assertThat(matcher).isSameAs(pathMatcher);
};
RequestMappingHandlerMapping annotationsMapping = webMvcConfig.requestMappingHandlerMapping(
webMvcConfig.mvcContentNegotiationManager(),
webMvcConfig.mvcConversionService(),
webMvcConfig.mvcResourceUrlProvider());
assertThat(annotationsMapping).isNotNull();
assertThat(annotationsMapping.useRegisteredSuffixPatternMatch()).isEqualTo(true);
assertThat(annotationsMapping.useSuffixPatternMatch()).isEqualTo(true);
assertThat(annotationsMapping.useTrailingSlashMatch()).isEqualTo(false);
configAssertion.accept(annotationsMapping.getUrlPathHelper(), annotationsMapping.getPathMatcher());
SimpleUrlHandlerMapping mapping = (SimpleUrlHandlerMapping) webMvcConfig.viewControllerHandlerMapping(
webMvcConfig.mvcConversionService(),
webMvcConfig.mvcResourceUrlProvider());
assertThat(mapping).isNotNull();
configAssertion.accept(mapping.getUrlPathHelper(), mapping.getPathMatcher());
mapping = (SimpleUrlHandlerMapping) webMvcConfig.resourceHandlerMapping(
webMvcConfig.mvcContentNegotiationManager(),
webMvcConfig.mvcConversionService(),
webMvcConfig.mvcResourceUrlProvider());
assertThat(mapping).isNotNull();
configAssertion.accept(mapping.getUrlPathHelper(), mapping.getPathMatcher());
configAssertion.accept(
webMvcConfig.mvcResourceUrlProvider().getUrlPathHelper(),
webMvcConfig.mvcResourceUrlProvider().getPathMatcher());
configAssertion.accept(webMvcConfig.mvcUrlPathHelper(), webMvcConfig.mvcPathMatcher());
}
@Test
public void configurePathPatternParser() {
PathPatternParser patternParser = new PathPatternParser();
PathMatcher pathMatcher = mock(PathMatcher.class);
UrlPathHelper pathHelper = mock(UrlPathHelper.class);
WebMvcConfigurer configurer = new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setPatternParser(patternParser)
.setUrlPathHelper(pathHelper)
.setPathMatcher(pathMatcher);
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/");
}
};
MockServletContext servletContext = new MockServletContext();
webMvcConfig.setConfigurers(Collections.singletonList(configurer));
webMvcConfig.setServletContext(servletContext);
webMvcConfig.setApplicationContext(new GenericWebApplicationContext(servletContext));
BiConsumer<UrlPathHelper, PathMatcher> configAssertion = (helper, matcher) -> {
assertThat(helper).isNotSameAs(pathHelper);
assertThat(matcher).isNotSameAs(pathMatcher);
};
RequestMappingHandlerMapping annotationsMapping = webMvcConfig.requestMappingHandlerMapping(
webMvcConfig.mvcContentNegotiationManager(),
webMvcConfig.mvcConversionService(),
webMvcConfig.mvcResourceUrlProvider());
assertThat(annotationsMapping).isNotNull();
assertThat(annotationsMapping.getPatternParser()).isSameAs(patternParser);
configAssertion.accept(annotationsMapping.getUrlPathHelper(), annotationsMapping.getPathMatcher());
SimpleUrlHandlerMapping mapping = (SimpleUrlHandlerMapping) webMvcConfig.viewControllerHandlerMapping(
webMvcConfig.mvcConversionService(),
webMvcConfig.mvcResourceUrlProvider());
assertThat(mapping).isNotNull();
assertThat(mapping.getPatternParser()).isSameAs(patternParser);
configAssertion.accept(mapping.getUrlPathHelper(), mapping.getPathMatcher());
mapping = (SimpleUrlHandlerMapping) webMvcConfig.resourceHandlerMapping(
webMvcConfig.mvcContentNegotiationManager(),
webMvcConfig.mvcConversionService(),
webMvcConfig.mvcResourceUrlProvider());
assertThat(mapping).isNotNull();
assertThat(mapping.getPatternParser()).isSameAs(patternParser);
configAssertion.accept(mapping.getUrlPathHelper(), mapping.getPathMatcher());
assertThat(webMvcConfig.mvcResourceUrlProvider().getUrlPathHelper()).isSameAs(pathHelper);
assertThat(webMvcConfig.mvcResourceUrlProvider().getPathMatcher()).isSameAs(pathMatcher);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -150,10 +150,9 @@ public class WebMvcConfigurationSupportExtensionTests {
.findFirst()
.orElseThrow(() -> new AssertionError("UserController bean not found"))
.getKey();
assertThat(info.getPatternsCondition().getPatterns()).isEqualTo(Collections.singleton("/api/user/{id}"));
assertThat(info.getPatternValues()).isEqualTo(Collections.singleton("/api/user/{id}"));
AbstractHandlerMapping handlerMapping = (AbstractHandlerMapping) this.config.viewControllerHandlerMapping(
this.config.mvcPathMatcher(), this.config.mvcUrlPathHelper(),
this.config.mvcConversionService(), this.config.mvcResourceUrlProvider());
handlerMapping.setApplicationContext(this.context);
assertThat(handlerMapping).isNotNull();
@@ -171,7 +170,6 @@ public class WebMvcConfigurationSupportExtensionTests {
assertThat(chain.getHandler()).isNotNull();
handlerMapping = (AbstractHandlerMapping) this.config.resourceHandlerMapping(
this.config.mvcUrlPathHelper(), this.config.mvcPathMatcher(),
this.config.mvcContentNegotiationManager(), this.config.mvcConversionService(),
this.config.mvcResourceUrlProvider());
handlerMapping.setApplicationContext(this.context);
@@ -290,7 +288,6 @@ public class WebMvcConfigurationSupportExtensionTests {
request.setRequestURI("/resources/foo.gif");
SimpleUrlHandlerMapping handlerMapping = (SimpleUrlHandlerMapping) this.config.resourceHandlerMapping(
this.config.mvcUrlPathHelper(), this.config.mvcPathMatcher(),
this.config.mvcContentNegotiationManager(), this.config.mvcConversionService(),
this.config.mvcResourceUrlProvider());
handlerMapping.setApplicationContext(this.context);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -156,7 +156,7 @@ public class WebMvcConfigurationSupportTests {
}
@Test
public void requestMappingHandlerAdapter() throws Exception {
public void requestMappingHandlerAdapter() {
ApplicationContext context = initContext(WebConfig.class);
RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
@@ -196,7 +196,7 @@ public class WebMvcConfigurationSupportTests {
}
@Test
public void uriComponentsContributor() throws Exception {
public void uriComponentsContributor() {
ApplicationContext context = initContext(WebConfig.class);
CompositeUriComponentsContributor uriComponentsContributor = context.getBean(
MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME,
@@ -207,7 +207,7 @@ public class WebMvcConfigurationSupportTests {
@Test
@SuppressWarnings("unchecked")
public void handlerExceptionResolver() throws Exception {
public void handlerExceptionResolver() {
ApplicationContext context = initContext(WebConfig.class);
HandlerExceptionResolverComposite compositeResolver =
context.getBean("handlerExceptionResolver", HandlerExceptionResolverComposite.class);
@@ -300,7 +300,7 @@ public class WebMvcConfigurationSupportTests {
}
@Test
public void defaultPathMatchConfiguration() throws Exception {
public void defaultPathMatchConfiguration() {
ApplicationContext context = initContext(WebConfig.class);
UrlPathHelper urlPathHelper = context.getBean(UrlPathHelper.class);
PathMatcher pathMatcher = context.getBean(PathMatcher.class);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import static org.assertj.core.api.Assertions.assertThat;
@@ -35,14 +36,15 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Arjen Poutsma
*/
public class DefaultServerRequestBuilderTests {
class DefaultServerRequestBuilderTests {
private final List<HttpMessageConverter<?>> messageConverters =
Collections.singletonList(new StringHttpMessageConverter());
private final List<HttpMessageConverter<?>> messageConverters = Collections.singletonList(
new StringHttpMessageConverter());
@Test
public void from() throws ServletException, IOException {
MockHttpServletRequest request = new MockHttpServletRequest("POST", "https://example.com");
void from() throws ServletException, IOException {
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("POST", "https://example.com", true);
request.addHeader("foo", "bar");
ServerRequest other = ServerRequest.create(request, messageConverters);
@@ -65,7 +67,7 @@ public class DefaultServerRequestBuilderTests {
assertThat(result.cookies().size()).isEqualTo(2);
assertThat(result.cookies().getFirst("foo").getValue()).isEqualTo("bar");
assertThat(result.cookies().getFirst("baz").getValue()).isEqualTo("qux");
assertThat(result.attributes().size()).isEqualTo(2);
assertThat(result.attributes().size()).isEqualTo(other.attributes().size() + 2);
assertThat(result.attributes().get("foo")).isEqualTo("bar");
assertThat(result.attributes().get("baz")).isEqualTo("qux");

View File

@@ -51,6 +51,7 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.server.MockServerWebExchange;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpSession;
@@ -66,23 +67,22 @@ import static org.springframework.web.testfixture.http.server.reactive.MockServe
* @author Arjen Poutsma
* @since 5.1
*/
public class DefaultServerRequestTests {
class DefaultServerRequestTests {
private final List<HttpMessageConverter<?>> messageConverters = Collections.singletonList(
new StringHttpMessageConverter());
@Test
public void method() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("HEAD", "/");
DefaultServerRequest request =
new DefaultServerRequest(servletRequest, this.messageConverters);
void method() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("HEAD", "/", true);
DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters);
assertThat(request.method()).isEqualTo(HttpMethod.HEAD);
}
@Test
public void uri() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void uri() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.setServerName("example.com");
servletRequest.setScheme("https");
servletRequest.setServerPort(443);
@@ -94,8 +94,8 @@ public class DefaultServerRequestTests {
}
@Test
public void uriBuilder() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/path");
void uriBuilder() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/path", true);
servletRequest.setQueryString("a=1");
DefaultServerRequest request =
@@ -110,8 +110,8 @@ public class DefaultServerRequestTests {
}
@Test
public void attribute() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void attribute() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.setAttribute("foo", "bar");
DefaultServerRequest request =
@@ -121,8 +121,8 @@ public class DefaultServerRequestTests {
}
@Test
public void params() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void params() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.setParameter("foo", "bar");
DefaultServerRequest request =
@@ -132,11 +132,11 @@ public class DefaultServerRequestTests {
}
@Test
public void multipartData() throws Exception {
void multipartData() throws Exception {
MockPart formPart = new MockPart("form", "foo".getBytes(UTF_8));
MockPart filePart = new MockPart("file", "foo.txt", "foo".getBytes(UTF_8));
MockHttpServletRequest servletRequest = new MockHttpServletRequest("POST", "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("POST", "/", true);
servletRequest.addPart(formPart);
servletRequest.addPart(filePart);
@@ -151,8 +151,8 @@ public class DefaultServerRequestTests {
}
@Test
public void emptyQueryParam() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void emptyQueryParam() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.setParameter("foo", "");
DefaultServerRequest request =
@@ -162,8 +162,8 @@ public class DefaultServerRequestTests {
}
@Test
public void absentQueryParam() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void absentQueryParam() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.setParameter("foo", "");
DefaultServerRequest request =
@@ -173,8 +173,8 @@ public class DefaultServerRequestTests {
}
@Test
public void pathVariable() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void pathVariable() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
Map<String, String> pathVariables = Collections.singletonMap("foo", "bar");
servletRequest
.setAttribute(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathVariables);
@@ -186,8 +186,8 @@ public class DefaultServerRequestTests {
}
@Test
public void pathVariableNotFound() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void pathVariableNotFound() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
Map<String, String> pathVariables = Collections.singletonMap("foo", "bar");
servletRequest
.setAttribute(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathVariables);
@@ -200,8 +200,8 @@ public class DefaultServerRequestTests {
}
@Test
public void pathVariables() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void pathVariables() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
Map<String, String> pathVariables = Collections.singletonMap("foo", "bar");
servletRequest
.setAttribute(RouterFunctions.URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathVariables);
@@ -213,7 +213,7 @@ public class DefaultServerRequestTests {
}
@Test
public void header() {
void header() {
HttpHeaders httpHeaders = new HttpHeaders();
List<MediaType> accept =
Collections.singletonList(MediaType.APPLICATION_JSON);
@@ -229,7 +229,7 @@ public class DefaultServerRequestTests {
List<HttpRange> range = Collections.singletonList(HttpRange.createByteRange(0, 42));
httpHeaders.setRange(range);
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
httpHeaders.forEach(servletRequest::addHeader);
servletRequest.setContentType(MediaType.TEXT_PLAIN_VALUE);
@@ -247,10 +247,10 @@ public class DefaultServerRequestTests {
}
@Test
public void cookies() {
void cookies() {
Cookie cookie = new Cookie("foo", "bar");
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.setCookies(cookie);
DefaultServerRequest request = new DefaultServerRequest(servletRequest,
@@ -264,8 +264,8 @@ public class DefaultServerRequestTests {
}
@Test
public void bodyClass() throws Exception {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void bodyClass() throws Exception {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.setContentType(MediaType.TEXT_PLAIN_VALUE);
servletRequest.setContent("foo".getBytes(UTF_8));
@@ -277,8 +277,8 @@ public class DefaultServerRequestTests {
}
@Test
public void bodyParameterizedTypeReference() throws Exception {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void bodyParameterizedTypeReference() throws Exception {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.setContentType(MediaType.APPLICATION_JSON_VALUE);
servletRequest.setContent("[\"foo\",\"bar\"]".getBytes(UTF_8));
@@ -292,8 +292,8 @@ public class DefaultServerRequestTests {
}
@Test
public void bodyUnacceptable() throws Exception {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void bodyUnacceptable() throws Exception {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.setContentType(MediaType.TEXT_PLAIN_VALUE);
servletRequest.setContent("foo".getBytes(UTF_8));
@@ -305,8 +305,8 @@ public class DefaultServerRequestTests {
}
@Test
public void session() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void session() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
MockHttpSession session = new MockHttpSession();
servletRequest.setSession(session);
@@ -318,8 +318,8 @@ public class DefaultServerRequestTests {
}
@Test
public void principal() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
void principal() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
Principal principal = new Principal() {
@Override
public String getName() {
@@ -336,7 +336,7 @@ public class DefaultServerRequestTests {
@ParameterizedHttpMethodTest
void checkNotModifiedTimestamp(String method) throws Exception {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
servletRequest.addHeader(HttpHeaders.IF_MODIFIED_SINCE, now.toEpochMilli());
@@ -352,7 +352,7 @@ public class DefaultServerRequestTests {
@ParameterizedHttpMethodTest
void checkModifiedTimestamp(String method) {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
Instant oneMinuteAgo = now.minus(1, ChronoUnit.MINUTES);
servletRequest.addHeader(HttpHeaders.IF_MODIFIED_SINCE, oneMinuteAgo.toEpochMilli());
@@ -366,7 +366,7 @@ public class DefaultServerRequestTests {
@ParameterizedHttpMethodTest
void checkNotModifiedETag(String method) {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
String eTag = "\"Foo\"";
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, eTag);
@@ -382,7 +382,7 @@ public class DefaultServerRequestTests {
@ParameterizedHttpMethodTest
void checkNotModifiedETagWithSeparatorChars(String method) {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
String eTag = "\"Foo, Bar\"";
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, eTag);
@@ -398,7 +398,7 @@ public class DefaultServerRequestTests {
@ParameterizedHttpMethodTest
void checkModifiedETag(String method) {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
String currentETag = "\"Foo\"";
String oldEtag = "Bar";
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, oldEtag);
@@ -412,7 +412,7 @@ public class DefaultServerRequestTests {
@ParameterizedHttpMethodTest
void checkNotModifiedUnpaddedETag(String method) {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
String eTag = "Foo";
String paddedEtag = String.format("\"%s\"", eTag);
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, paddedEtag);
@@ -429,7 +429,7 @@ public class DefaultServerRequestTests {
@ParameterizedHttpMethodTest
void checkModifiedUnpaddedETag(String method) {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
String currentETag = "Foo";
String oldEtag = "Bar";
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, oldEtag);
@@ -443,7 +443,7 @@ public class DefaultServerRequestTests {
@ParameterizedHttpMethodTest
void checkNotModifiedWildcardIsIgnored(String method) {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
String eTag = "\"Foo\"";
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, "*");
DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters);
@@ -455,7 +455,7 @@ public class DefaultServerRequestTests {
@ParameterizedHttpMethodTest
void checkNotModifiedETagAndTimestamp(String method) {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
String eTag = "\"Foo\"";
Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, eTag);
@@ -475,7 +475,7 @@ public class DefaultServerRequestTests {
@ParameterizedHttpMethodTest
void checkNotModifiedETagAndModifiedTimestamp(String method) {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
String eTag = "\"Foo\"";
Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
Instant oneMinuteAgo = now.minus(1, ChronoUnit.MINUTES);
@@ -498,8 +498,8 @@ public class DefaultServerRequestTests {
}
@ParameterizedHttpMethodTest
void checkModifiedETagAndNotModifiedTimestamp(String method) throws Exception {
MockHttpServletRequest servletRequest = new MockHttpServletRequest(method, "/");
void checkModifiedETagAndNotModifiedTimestamp(String method) {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest(method, "/", true);
String currentETag = "\"Foo\"";
String oldEtag = "\"Bar\"";
Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -25,26 +25,20 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Arjen Poutsma
*/
public class PathResourceLookupFunctionTests {
class PathResourceLookupFunctionTests {
@Test
public void normal() throws Exception {
ClassPathResource location =
new ClassPathResource("org/springframework/web/servlet/function/");
PathResourceLookupFunction function =
new PathResourceLookupFunction("/resources/**", location);
MockHttpServletRequest servletRequest =
new MockHttpServletRequest("GET", "/resources/response.txt");
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.emptyList());
void normal() throws Exception {
ClassPathResource location = new ClassPathResource("org/springframework/web/servlet/function/");
PathResourceLookupFunction function = new PathResourceLookupFunction("/resources/**", location);
ServerRequest request = initRequest("GET", "/resources/response.txt");
Optional<Resource> result = function.apply(request);
assertThat(result.isPresent()).isTrue();
@@ -54,44 +48,30 @@ public class PathResourceLookupFunctionTests {
}
@Test
public void subPath() throws Exception {
ClassPathResource location =
new ClassPathResource("org/springframework/web/servlet/function/");
PathResourceLookupFunction function =
new PathResourceLookupFunction("/resources/**", location);
MockHttpServletRequest servletRequest =
new MockHttpServletRequest("GET", "/resources/child/response.txt");
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.emptyList());
void subPath() throws Exception {
ClassPathResource location = new ClassPathResource("org/springframework/web/servlet/function/");
PathResourceLookupFunction function = new PathResourceLookupFunction("/resources/**", location);
ServerRequest request = initRequest("GET", "/resources/child/response.txt");
Optional<Resource> result = function.apply(request);
assertThat(result.isPresent()).isTrue();
File expected =
new ClassPathResource("org/springframework/web/servlet/function/child/response.txt")
.getFile();
File expected = new ClassPathResource("org/springframework/web/servlet/function/child/response.txt").getFile();
assertThat(result.get().getFile()).isEqualTo(expected);
}
@Test
public void notFound() {
ClassPathResource location =
new ClassPathResource("org/springframework/web/reactive/function/server/");
PathResourceLookupFunction function =
new PathResourceLookupFunction("/resources/**", location);
MockHttpServletRequest servletRequest =
new MockHttpServletRequest("GET", "/resources/foo.txt");
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.emptyList());
void notFound() {
ClassPathResource location = new ClassPathResource("org/springframework/web/reactive/function/server/");
PathResourceLookupFunction function = new PathResourceLookupFunction("/resources/**", location);
ServerRequest request = initRequest("GET", "/resources/foo.txt");
Optional<Resource> result = function.apply(request);
assertThat(result.isPresent()).isFalse();
}
@Test
public void composeResourceLookupFunction() throws Exception {
void composeResourceLookupFunction() throws Exception {
ClassPathResource defaultResource = new ClassPathResource("response.txt", getClass());
Function<ServerRequest, Optional<Resource>> lookupFunction =
@@ -108,8 +88,7 @@ public class PathResourceLookupFunctionTests {
}
});
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/resources/foo");
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.emptyList());
ServerRequest request = initRequest("GET", "/resources/foo");
Optional<Resource> result = customLookupFunction.apply(request);
assertThat(result.isPresent()).isTrue();
@@ -117,4 +96,10 @@ public class PathResourceLookupFunctionTests {
assertThat(result.get().getFile()).isEqualTo(defaultResource.getFile());
}
private ServerRequest initRequest(String httpMethod, String requestUri) {
return new DefaultServerRequest(
PathPatternsTestUtils.initRequest(httpMethod, requestUri, true),
Collections.emptyList());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -21,6 +21,7 @@ import java.util.Collections;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import static org.assertj.core.api.Assertions.assertThat;
@@ -28,18 +29,18 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Arjen Poutsma
*/
public class RequestPredicateTests {
class RequestPredicateTests {
private ServerRequest request;
@BeforeEach
public void createRequest() {
this.request = new DefaultServerRequest(new MockHttpServletRequest(),
Collections.emptyList());
void createRequest() {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
this.request = new DefaultServerRequest(servletRequest, Collections.emptyList());
}
@Test
public void and() {
void and() {
RequestPredicate predicate1 = request -> true;
RequestPredicate predicate2 = request -> true;
RequestPredicate predicate3 = request -> false;
@@ -50,7 +51,7 @@ public class RequestPredicateTests {
}
@Test
public void negate() {
void negate() {
RequestPredicate predicate = request -> false;
RequestPredicate negated = predicate.negate();
@@ -63,7 +64,7 @@ public class RequestPredicateTests {
}
@Test
public void or() {
void or() {
RequestPredicate predicate1 = request -> true;
RequestPredicate predicate2 = request -> false;
RequestPredicate predicate3 = request -> false;

View File

@@ -17,6 +17,7 @@
package org.springframework.web.servlet.function;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.function.Function;
import org.junit.jupiter.api.Test;
@@ -24,245 +25,189 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.pattern.PathPatternParser;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.http.MediaType.TEXT_XML_VALUE;
/**
* @author Arjen Poutsma
*/
public class RequestPredicatesTests {
class RequestPredicatesTests {
@Test
public void all() {
void all() {
RequestPredicate predicate = RequestPredicates.all();
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest request = initRequest("GET", "/");
assertThat(predicate.test(request)).isTrue();
}
@Test
public void method() {
void method() {
HttpMethod httpMethod = HttpMethod.GET;
RequestPredicate predicate = RequestPredicates.method(httpMethod);
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "https://example.com");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
assertThat(predicate.test(request)).isTrue();
servletRequest.setMethod("POST");
assertThat(predicate.test(request)).isFalse();
assertThat(predicate.test(initRequest("GET", "https://example.com"))).isTrue();
assertThat(predicate.test(initRequest("POST", "https://example.com"))).isFalse();
}
@Test
public void methodCorsPreFlight() {
void methodCorsPreFlight() {
RequestPredicate predicate = RequestPredicates.method(HttpMethod.PUT);
MockHttpServletRequest servletRequest = new MockHttpServletRequest("OPTIONS", "https://example.com");
servletRequest.addHeader("Origin", "https://example.com");
servletRequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "PUT");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest request = initRequest("OPTIONS", "https://example.com", servletRequest -> {
servletRequest.addHeader("Origin", "https://example.com");
servletRequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "PUT");
});
assertThat(predicate.test(request)).isTrue();
servletRequest.removeHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
servletRequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST");
request = new DefaultServerRequest(servletRequest, emptyList());
request = initRequest("OPTIONS", "https://example.com", servletRequest -> {
servletRequest.removeHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD);
servletRequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST");
});
assertThat(predicate.test(request)).isFalse();
}
@Test
public void methods() {
void methods() {
RequestPredicate predicate = RequestPredicates.methods(HttpMethod.GET, HttpMethod.HEAD);
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "https://example.com");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
assertThat(predicate.test(request)).isTrue();
servletRequest.setMethod("HEAD");
assertThat(predicate.test(request)).isTrue();
servletRequest.setMethod("POST");
assertThat(predicate.test(request)).isFalse();
assertThat(predicate.test(initRequest("GET", "https://example.com"))).isTrue();
assertThat(predicate.test(initRequest("HEAD", "https://example.com"))).isTrue();
assertThat(predicate.test(initRequest("POST", "https://example.com"))).isFalse();
}
@Test
public void allMethods() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/path");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
void allMethods() {
RequestPredicate predicate = RequestPredicates.GET("/p*");
assertThat(predicate.test(request)).isTrue();
assertThat(predicate.test(initRequest("GET", "/path"))).isTrue();
predicate = RequestPredicates.HEAD("/p*");
servletRequest.setMethod("HEAD");
assertThat(predicate.test(request)).isTrue();
assertThat(predicate.test(initRequest("HEAD", "/path"))).isTrue();
predicate = RequestPredicates.POST("/p*");
servletRequest.setMethod("POST");
assertThat(predicate.test(request)).isTrue();
assertThat(predicate.test(initRequest("POST", "/path"))).isTrue();
predicate = RequestPredicates.PUT("/p*");
servletRequest.setMethod("PUT");
assertThat(predicate.test(request)).isTrue();
assertThat(predicate.test(initRequest("PUT", "/path"))).isTrue();
predicate = RequestPredicates.PATCH("/p*");
servletRequest.setMethod("PATCH");
assertThat(predicate.test(request)).isTrue();
assertThat(predicate.test(initRequest("PATCH", "/path"))).isTrue();
predicate = RequestPredicates.DELETE("/p*");
servletRequest.setMethod("DELETE");
assertThat(predicate.test(request)).isTrue();
assertThat(predicate.test(initRequest("DELETE", "/path"))).isTrue();
predicate = RequestPredicates.OPTIONS("/p*");
servletRequest.setMethod("OPTIONS");
assertThat(predicate.test(request)).isTrue();
assertThat(predicate.test(initRequest("OPTIONS", "/path"))).isTrue();
}
@Test
public void path() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/path");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
void path() {
RequestPredicate predicate = RequestPredicates.path("/p*");
assertThat(predicate.test(request)).isTrue();
servletRequest = new MockHttpServletRequest("GET", "/foo");
request = new DefaultServerRequest(servletRequest, emptyList());
assertThat(predicate.test(request)).isFalse();
assertThat(predicate.test(initRequest("GET", "/path"))).isTrue();
assertThat(predicate.test(initRequest("GET", "/foo"))).isFalse();
}
@Test
public void servletPath() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/foo/bar");
servletRequest.setServletPath("/foo");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
void contextPath() {
RequestPredicate predicate = RequestPredicates.path("/bar");
ServerRequest request = new DefaultServerRequest(
PathPatternsTestUtils.initRequest("GET", "/foo", "/bar", true),
Collections.emptyList());
assertThat(predicate.test(request)).isTrue();
servletRequest = new MockHttpServletRequest("GET", "/foo");
request = new DefaultServerRequest(servletRequest, emptyList());
request = initRequest("GET", "/foo");
assertThat(predicate.test(request)).isFalse();
}
@Test
public void pathNoLeadingSlash() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/path");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
void pathNoLeadingSlash() {
RequestPredicate predicate = RequestPredicates.path("p*");
assertThat(predicate.test(request)).isTrue();
assertThat(predicate.test(initRequest("GET", "/path"))).isTrue();
}
@Test
public void pathEncoded() {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/foo%20bar");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
void pathEncoded() {
RequestPredicate predicate = RequestPredicates.path("/foo bar");
assertThat(predicate.test(request)).isTrue();
servletRequest = new MockHttpServletRequest();
request = new DefaultServerRequest(servletRequest, emptyList());
assertThat(predicate.test(request)).isFalse();
assertThat(predicate.test(initRequest("GET", "/foo%20bar"))).isTrue();
assertThat(predicate.test(initRequest("GET", ""))).isFalse();
}
@Test
public void pathPredicates() {
void pathPredicates() {
PathPatternParser parser = new PathPatternParser();
parser.setCaseSensitive(false);
Function<String, RequestPredicate> pathPredicates = RequestPredicates.pathPredicates(parser);
RequestPredicate predicate = pathPredicates.apply("/P*");
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/path");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
assertThat(predicate.test(request)).isTrue();
assertThat(predicate.test(initRequest("GET", "/path"))).isTrue();
}
@Test
public void headers() {
void headers() {
String name = "MyHeader";
String value = "MyValue";
RequestPredicate predicate =
RequestPredicates.headers(
headers -> headers.header(name).equals(Collections.singletonList(value)));
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/path");
servletRequest.addHeader(name, value);
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest request = initRequest("GET", "/path", req -> req.addHeader(name, value));
assertThat(predicate.test(request)).isTrue();
servletRequest = new MockHttpServletRequest();
request = new DefaultServerRequest(servletRequest, emptyList());
assertThat(predicate.test(request)).isFalse();
assertThat(predicate.test(initRequest("GET", ""))).isFalse();
}
@Test
public void headersCors() {
void headersCors() {
RequestPredicate predicate = RequestPredicates.headers(headers -> false);
MockHttpServletRequest servletRequest = new MockHttpServletRequest("OPTIONS", "https://example.com");
servletRequest.addHeader("Origin", "https://example.com");
servletRequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "PUT");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest request = initRequest("OPTIONS", "https://example.com", servletRequest -> {
servletRequest.addHeader("Origin", "https://example.com");
servletRequest.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "PUT");
});
assertThat(predicate.test(request)).isTrue();
}
@Test
public void contentType() {
void contentType() {
MediaType json = MediaType.APPLICATION_JSON;
RequestPredicate predicate = RequestPredicates.contentType(json);
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/path");
servletRequest.setContentType(json.toString());
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest request = initRequest("GET", "/path", req -> req.setContentType(json.toString()));
assertThat(predicate.test(request)).isTrue();
servletRequest = new MockHttpServletRequest();
request = new DefaultServerRequest(servletRequest, emptyList());
assertThat(predicate.test(request)).isFalse();
assertThat(predicate.test(initRequest("GET", ""))).isFalse();
}
@Test
public void accept() {
void accept() {
MediaType json = MediaType.APPLICATION_JSON;
RequestPredicate predicate = RequestPredicates.accept(json);
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/path");
servletRequest.addHeader("Accept", json.toString());
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest request = initRequest("GET", "/path", req -> req.addHeader("Accept", json.toString()));
assertThat(predicate.test(request)).isTrue();
servletRequest = new MockHttpServletRequest();
servletRequest.addHeader("Accept", TEXT_XML_VALUE);
request = new DefaultServerRequest(servletRequest, emptyList());
request = initRequest("GET", "", req -> req.addHeader("Accept", TEXT_XML_VALUE));
assertThat(predicate.test(request)).isFalse();
}
@Test
public void pathExtension() {
void pathExtension() {
RequestPredicate predicate = RequestPredicates.pathExtension("txt");
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/file.txt");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
assertThat(predicate.test(request)).isTrue();
servletRequest = new MockHttpServletRequest("GET", "/FILE.TXT");
request = new DefaultServerRequest(servletRequest, emptyList());
assertThat(predicate.test(request)).isTrue();
assertThat(predicate.test(initRequest("GET", "/file.txt"))).isTrue();
assertThat(predicate.test(initRequest("GET", "/FILE.TXT"))).isTrue();
predicate = RequestPredicates.pathExtension("bar");
assertThat(predicate.test(request)).isFalse();
assertThat(predicate.test(initRequest("GET", "/FILE.TXT"))).isFalse();
servletRequest = new MockHttpServletRequest("GET", "/file.foo");
request = new DefaultServerRequest(servletRequest, emptyList());
assertThat(predicate.test(request)).isFalse();
assertThat(predicate.test(initRequest("GET", "/file.foo"))).isFalse();
}
@Test
public void param() {
void param() {
RequestPredicate predicate = RequestPredicates.param("foo", "bar");
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/path");
servletRequest.addParameter("foo", "bar");
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest request = initRequest("GET", "/path", req -> req.addParameter("foo", "bar"));
assertThat(predicate.test(request)).isTrue();
predicate = RequestPredicates.param("foo", s -> s.equals("bar"));
@@ -275,4 +220,17 @@ public class RequestPredicatesTests {
assertThat(predicate.test(request)).isFalse();
}
private ServerRequest initRequest(String httpMethod, String requestUri) {
return initRequest(httpMethod, requestUri, null);
}
private ServerRequest initRequest(
String httpMethod, String requestUri, @Nullable Consumer<MockHttpServletRequest> initializer) {
return new DefaultServerRequest(
PathPatternsTestUtils.initRequest(httpMethod, null, requestUri, true, initializer),
Collections.emptyList());
}
}

View File

@@ -22,7 +22,6 @@ import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import javax.servlet.ServletException;
@@ -35,10 +34,10 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.ResourceRegionHttpMessageConverter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
@@ -61,19 +60,13 @@ public class ResourceHandlerFunctionTests {
public void createContext() {
this.messageConverter = new ResourceHttpMessageConverter();
ResourceRegionHttpMessageConverter regionConverter = new ResourceRegionHttpMessageConverter();
this.context = new ServerResponse.Context() {
@Override
public List<HttpMessageConverter<?>> messageConverters() {
return Arrays.asList(messageConverter, regionConverter);
}
};
this.context = () -> Arrays.asList(messageConverter, regionConverter);
}
@Test
public void get() throws IOException, ServletException {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.singletonList(messageConverter));
ServerResponse response = this.handlerFunction.handle(request);
@@ -97,7 +90,7 @@ public class ResourceHandlerFunctionTests {
@Test
public void getRange() throws IOException, ServletException {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.addHeader("Range", "bytes=0-5");
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.singletonList(messageConverter));
@@ -126,7 +119,7 @@ public class ResourceHandlerFunctionTests {
@Test
public void getInvalidRange() throws IOException, ServletException {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.addHeader("Range", "bytes=0-10, 0-10, 0-10, 0-10, 0-10, 0-10");
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.singletonList(messageConverter));
@@ -152,7 +145,7 @@ public class ResourceHandlerFunctionTests {
@Test
public void head() throws IOException, ServletException {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("HEAD", "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("HEAD", "/", true);
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.singletonList(messageConverter));
ServerResponse response = this.handlerFunction.handle(request);
@@ -174,10 +167,9 @@ public class ResourceHandlerFunctionTests {
assertThat(servletResponse.getContentLength()).isEqualTo(this.resource.contentLength());
}
@Test
public void options() throws ServletException, IOException {
MockHttpServletRequest servletRequest = new MockHttpServletRequest("OPTIONS", "/");
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("OPTIONS", "/", true);
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.singletonList(messageConverter));
ServerResponse response = this.handlerFunction.handle(request);
@@ -194,5 +186,4 @@ public class ResourceHandlerFunctionTests {
assertThat(actualBytes.length).isEqualTo(0);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -18,6 +18,7 @@ package org.springframework.web.servlet.function;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
@@ -25,6 +26,8 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import static java.util.Collections.emptyList;
@@ -36,10 +39,10 @@ import static org.springframework.web.servlet.function.RequestPredicates.HEAD;
*
* @author Arjen Poutsma
*/
public class RouterFunctionBuilderTests {
class RouterFunctionBuilderTests {
@Test
public void route() {
void route() {
RouterFunction<ServerResponse> route = RouterFunctions.route()
.GET("/foo", request -> ServerResponse.ok().build())
.POST("/", RequestPredicates.contentType(MediaType.TEXT_PLAIN),
@@ -47,8 +50,7 @@ public class RouterFunctionBuilderTests {
.route(HEAD("/foo"), request -> ServerResponse.accepted().build())
.build();
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/foo");
ServerRequest getFooRequest = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest getFooRequest = initRequest("GET", "/foo");
Optional<Integer> responseStatus = route.route(getFooRequest)
.map(handlerFunction -> handle(handlerFunction, getFooRequest))
@@ -56,8 +58,7 @@ public class RouterFunctionBuilderTests {
.map(HttpStatus::value);
assertThat(responseStatus.get().intValue()).isEqualTo(200);
servletRequest = new MockHttpServletRequest("HEAD", "/foo");
ServerRequest headFooRequest = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest headFooRequest = initRequest("HEAD", "/foo");
responseStatus = route.route(headFooRequest)
.map(handlerFunction -> handle(handlerFunction, getFooRequest))
@@ -65,9 +66,7 @@ public class RouterFunctionBuilderTests {
.map(HttpStatus::value);
assertThat(responseStatus.get().intValue()).isEqualTo(202);
servletRequest = new MockHttpServletRequest("POST", "/");
servletRequest.setContentType("text/plain");
ServerRequest barRequest = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest barRequest = initRequest("POST", "/", req -> req.setContentType("text/plain"));
responseStatus = route.route(barRequest)
.map(handlerFunction -> handle(handlerFunction, barRequest))
@@ -75,8 +74,7 @@ public class RouterFunctionBuilderTests {
.map(HttpStatus::value);
assertThat(responseStatus.get().intValue()).isEqualTo(204);
servletRequest = new MockHttpServletRequest("POST", "/");
ServerRequest invalidRequest = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest invalidRequest = initRequest("POST", "/");
responseStatus = route.route(invalidRequest)
.map(handlerFunction -> handle(handlerFunction, invalidRequest))
@@ -84,7 +82,6 @@ public class RouterFunctionBuilderTests {
.map(HttpStatus::value);
assertThat(responseStatus.isPresent()).isFalse();
}
private static ServerResponse handle(HandlerFunction<ServerResponse> handlerFunction,
@@ -98,7 +95,7 @@ public class RouterFunctionBuilderTests {
}
@Test
public void resources() {
void resources() {
Resource resource = new ClassPathResource("/org/springframework/web/servlet/function/");
assertThat(resource.exists()).isTrue();
@@ -106,9 +103,7 @@ public class RouterFunctionBuilderTests {
.resources("/resources/**", resource)
.build();
MockHttpServletRequest servletRequest =
new MockHttpServletRequest("GET", "/resources/response.txt");
ServerRequest resourceRequest = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest resourceRequest = initRequest("GET", "/resources/response.txt");
Optional<Integer> responseStatus = route.route(resourceRequest)
.map(handlerFunction -> handle(handlerFunction, resourceRequest))
@@ -116,8 +111,7 @@ public class RouterFunctionBuilderTests {
.map(HttpStatus::value);
assertThat(responseStatus.get().intValue()).isEqualTo(200);
servletRequest = new MockHttpServletRequest("POST", "/resources/foo.txt");
ServerRequest invalidRequest = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest invalidRequest = initRequest("POST", "/resources/foo.txt");
responseStatus = route.route(invalidRequest)
.map(handlerFunction -> handle(handlerFunction, invalidRequest))
@@ -127,7 +121,7 @@ public class RouterFunctionBuilderTests {
}
@Test
public void nest() {
void nest() {
RouterFunction<ServerResponse> route = RouterFunctions.route()
.path("/foo", builder ->
builder.path("/bar",
@@ -136,8 +130,7 @@ public class RouterFunctionBuilderTests {
.build()))
.build();
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/foo/bar/baz");
ServerRequest fooRequest = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest fooRequest = initRequest("GET", "/foo/bar/baz");
Optional<Integer> responseStatus = route.route(fooRequest)
.map(handlerFunction -> handle(handlerFunction, fooRequest))
@@ -147,7 +140,7 @@ public class RouterFunctionBuilderTests {
}
@Test
public void filters() {
void filters() {
AtomicInteger filterCount = new AtomicInteger();
RouterFunction<ServerResponse> route = RouterFunctions.route()
@@ -178,8 +171,7 @@ public class RouterFunctionBuilderTests {
.build())
.build();
MockHttpServletRequest servletRequest = new MockHttpServletRequest("GET", "/foo");
ServerRequest fooRequest = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest fooRequest = initRequest("GET", "/foo");
route.route(fooRequest)
.map(handlerFunction -> handle(handlerFunction, fooRequest));
@@ -187,8 +179,7 @@ public class RouterFunctionBuilderTests {
filterCount.set(0);
servletRequest = new MockHttpServletRequest("GET", "/bar");
ServerRequest barRequest = new DefaultServerRequest(servletRequest, emptyList());
ServerRequest barRequest = initRequest("GET", "/bar");
Optional<Integer> responseStatus = route.route(barRequest)
.map(handlerFunction -> handle(handlerFunction, barRequest))
@@ -197,4 +188,16 @@ public class RouterFunctionBuilderTests {
assertThat(responseStatus.get().intValue()).isEqualTo(500);
}
private ServerRequest initRequest(String httpMethod, String requestUri) {
return initRequest(httpMethod, requestUri, null);
}
private ServerRequest initRequest(
String httpMethod, String requestUri, @Nullable Consumer<MockHttpServletRequest> consumer) {
return new DefaultServerRequest(
PathPatternsTestUtils.initRequest(httpMethod, null, requestUri, true, consumer), emptyList());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -16,22 +16,26 @@
package org.springframework.web.servlet.function;
import java.util.Collections;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Arjen Poutsma
*/
public class RouterFunctionTests {
class RouterFunctionTests {
private final ServerRequest request = new DefaultServerRequest(
PathPatternsTestUtils.initRequest("GET", "", true), Collections.emptyList());
@Test
public void and() {
void and() {
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().build();
RouterFunction<ServerResponse> routerFunction1 = request -> Optional.empty();
RouterFunction<ServerResponse> routerFunction2 = request -> Optional.of(handlerFunction);
@@ -39,9 +43,6 @@ public class RouterFunctionTests {
RouterFunction<ServerResponse> result = routerFunction1.and(routerFunction2);
assertThat(result).isNotNull();
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
Optional<HandlerFunction<ServerResponse>> resultHandlerFunction = result.route(request);
assertThat(resultHandlerFunction.isPresent()).isTrue();
assertThat(resultHandlerFunction.get()).isEqualTo(handlerFunction);
@@ -49,7 +50,7 @@ public class RouterFunctionTests {
@Test
public void andOther() {
void andOther() {
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().body("42");
RouterFunction<?> routerFunction1 = request -> Optional.empty();
RouterFunction<ServerResponse> routerFunction2 = request -> Optional.of(handlerFunction);
@@ -57,9 +58,6 @@ public class RouterFunctionTests {
RouterFunction<?> result = routerFunction1.andOther(routerFunction2);
assertThat(result).isNotNull();
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
Optional<? extends HandlerFunction<?>> resultHandlerFunction = result.route(request);
assertThat(resultHandlerFunction.isPresent()).isTrue();
assertThat(resultHandlerFunction.get()).isEqualTo(handlerFunction);
@@ -67,23 +65,20 @@ public class RouterFunctionTests {
@Test
public void andRoute() {
void andRoute() {
RouterFunction<ServerResponse> routerFunction1 = request -> Optional.empty();
RequestPredicate requestPredicate = request -> true;
RouterFunction<ServerResponse> result = routerFunction1.andRoute(requestPredicate, this::handlerMethod);
assertThat(result).isNotNull();
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
Optional<? extends HandlerFunction<?>> resultHandlerFunction = result.route(request);
assertThat(resultHandlerFunction.isPresent()).isTrue();
}
@Test
public void filter() {
void filter() {
String string = "42";
HandlerFunction<EntityResponse<String>> handlerFunction =
request -> EntityResponse.fromObject(string).build();
@@ -100,9 +95,6 @@ public class RouterFunctionTests {
RouterFunction<EntityResponse<Integer>> result = routerFunction.filter(filterFunction);
assertThat(result).isNotNull();
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
ServerRequest request = new DefaultServerRequest(servletRequest, emptyList());
Optional<EntityResponse<Integer>> resultHandlerFunction = result.route(request)
.map(hf -> {
try {

View File

@@ -21,7 +21,7 @@ import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@@ -32,12 +32,14 @@ import static org.mockito.Mockito.mock;
*/
public class RouterFunctionsTests {
private final ServerRequest request = new DefaultServerRequest(
PathPatternsTestUtils.initRequest("GET", "", true), Collections.emptyList());
@Test
public void routeMatch() {
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().build();
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.emptyList());
RequestPredicate requestPredicate = mock(RequestPredicate.class);
given(requestPredicate.test(request)).willReturn(true);
@@ -54,8 +56,6 @@ public class RouterFunctionsTests {
public void routeNoMatch() {
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().build();
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.emptyList());
RequestPredicate requestPredicate = mock(RequestPredicate.class);
given(requestPredicate.test(request)).willReturn(false);
@@ -71,8 +71,6 @@ public class RouterFunctionsTests {
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().build();
RouterFunction<ServerResponse> routerFunction = request -> Optional.of(handlerFunction);
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.emptyList());
RequestPredicate requestPredicate = mock(RequestPredicate.class);
given(requestPredicate.nest(request)).willReturn(Optional.of(request));
@@ -89,8 +87,6 @@ public class RouterFunctionsTests {
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().build();
RouterFunction<ServerResponse> routerFunction = request -> Optional.of(handlerFunction);
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
ServerRequest request = new DefaultServerRequest(servletRequest, Collections.emptyList());
RequestPredicate requestPredicate = mock(RequestPredicate.class);
given(requestPredicate.nest(request)).willReturn(Optional.empty());

View File

@@ -16,22 +16,18 @@
package org.springframework.web.servlet.handler;
import java.io.IOException;
import java.util.Collections;
import java.util.stream.Stream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.util.ObjectUtils;
import org.springframework.http.server.RequestPath;
import org.springframework.lang.Nullable;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
@@ -39,6 +35,8 @@ import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.support.WebContentGenerator;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -50,205 +48,214 @@ import static org.mockito.Mockito.mock;
*/
class CorsAbstractHandlerMappingTests {
private MockHttpServletRequest request;
private AbstractHandlerMapping handlerMapping;
@BeforeEach
void setup() {
StaticWebApplicationContext context = new StaticWebApplicationContext();
this.handlerMapping = new TestHandlerMapping();
this.handlerMapping.setInterceptors(mock(HandlerInterceptor.class));
this.handlerMapping.setApplicationContext(context);
this.request = new MockHttpServletRequest();
this.request.setRemoteHost("domain1.com");
@SuppressWarnings("unused")
private static Stream<TestHandlerMapping> pathPatternsArguments() {
return Stream.of(new TestHandlerMapping(new PathPatternParser()), new TestHandlerMapping());
}
@Test
void actualRequestWithoutCorsConfigurationProvider() throws Exception {
this.request.setMethod(RequestMethod.GET.name());
this.request.setRequestURI("/foo");
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(this.request);
@PathPatternsParameterizedTest
void actualRequestWithoutCorsConfig(TestHandlerMapping mapping) throws Exception {
HandlerExecutionChain chain = mapping.getHandler(getCorsRequest("/foo"));
assertThat(chain).isNotNull();
assertThat(chain.getHandler()).isInstanceOf(SimpleHandler.class);
assertThat(mapping.hasSavedCorsConfig()).isFalse();
}
@Test
void preflightRequestWithoutCorsConfigurationProvider() throws Exception {
this.request.setMethod(RequestMethod.OPTIONS.name());
this.request.setRequestURI("/foo");
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(this.request);
@PathPatternsParameterizedTest
void preflightRequestWithoutCorsConfig(TestHandlerMapping mapping) throws Exception {
HandlerExecutionChain chain = mapping.getHandler(getPreFlightRequest("/foo"));
assertThat(chain).isNotNull();
assertThat(chain.getHandler()).isNotNull();
assertThat(chain.getHandler().getClass().getSimpleName()).isEqualTo("PreFlightHandler");
assertThat(mapping.hasSavedCorsConfig()).isFalse();
}
@Test
void actualRequestWithCorsConfigurationProvider() throws Exception {
this.request.setMethod(RequestMethod.GET.name());
this.request.setRequestURI("/cors");
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(this.request);
@PathPatternsParameterizedTest
void actualRequestWithCorsConfigProvider(TestHandlerMapping mapping) throws Exception {
HandlerExecutionChain chain = mapping.getHandler(getCorsRequest("/cors"));
assertThat(chain).isNotNull();
assertThat(chain.getHandler()).isInstanceOf(CorsAwareHandler.class);
assertThat(getRequiredCorsConfiguration(chain, false).getAllowedOrigins()).containsExactly("*");
assertThat(mapping.getRequiredCorsConfig().getAllowedOrigins()).containsExactly("*");
}
@Test // see gh-23843
void actualRequestWithCorsConfigurationProviderForHandlerChain() throws Exception {
this.request.setMethod(RequestMethod.GET.name());
this.request.setRequestURI("/chain");
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(this.request);
@PathPatternsParameterizedTest // see gh-23843
void actualRequestWithCorsConfigProviderForHandlerChain(TestHandlerMapping mapping) throws Exception {
HandlerExecutionChain chain = mapping.getHandler(getCorsRequest("/chain"));
assertThat(chain).isNotNull();
assertThat(chain.getHandler()).isInstanceOf(CorsAwareHandler.class);
assertThat(getRequiredCorsConfiguration(chain, false).getAllowedOrigins()).containsExactly("*");
assertThat(mapping.getRequiredCorsConfig().getAllowedOrigins()).containsExactly("*");
}
@Test
void preflightRequestWithCorsConfigurationProvider() throws Exception {
this.request.setMethod(RequestMethod.OPTIONS.name());
this.request.setRequestURI("/cors");
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(this.request);
@PathPatternsParameterizedTest
void preflightRequestWithCorsConfigProvider(TestHandlerMapping mapping) throws Exception {
HandlerExecutionChain chain = mapping.getHandler(getPreFlightRequest("/cors"));
assertThat(chain).isNotNull();
assertThat(chain.getHandler()).isNotNull();
assertThat(chain.getHandler().getClass().getSimpleName()).isEqualTo("PreFlightHandler");
assertThat(getRequiredCorsConfiguration(chain, true).getAllowedOrigins()).containsExactly("*");
assertThat(mapping.getRequiredCorsConfig().getAllowedOrigins()).containsExactly("*");
}
@Test
void actualRequestWithMappedCorsConfiguration() throws Exception {
@PathPatternsParameterizedTest
void actualRequestWithMappedCorsConfig(TestHandlerMapping mapping) throws Exception {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
this.handlerMapping.setCorsConfigurations(Collections.singletonMap("/foo", config));
this.request.setMethod(RequestMethod.GET.name());
this.request.setRequestURI("/foo");
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(this.request);
mapping.setCorsConfigurations(Collections.singletonMap("/foo", config));
HandlerExecutionChain chain = mapping.getHandler(getCorsRequest("/foo"));
assertThat(chain).isNotNull();
assertThat(chain.getHandler()).isInstanceOf(SimpleHandler.class);
assertThat(getRequiredCorsConfiguration(chain, false).getAllowedOrigins()).containsExactly("*");
assertThat(mapping.getRequiredCorsConfig().getAllowedOrigins()).containsExactly("*");
}
@Test
void preflightRequestWithMappedCorsConfiguration() throws Exception {
@PathPatternsParameterizedTest
void preflightRequestWithMappedCorsConfig(TestHandlerMapping mapping) throws Exception {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
this.handlerMapping.setCorsConfigurations(Collections.singletonMap("/foo", config));
this.request.setMethod(RequestMethod.OPTIONS.name());
this.request.setRequestURI("/foo");
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(this.request);
mapping.setCorsConfigurations(Collections.singletonMap("/foo", config));
HandlerExecutionChain chain = mapping.getHandler(getPreFlightRequest("/foo"));
assertThat(chain).isNotNull();
assertThat(chain.getHandler()).isNotNull();
assertThat(chain.getHandler().getClass().getSimpleName()).isEqualTo("PreFlightHandler");
assertThat(getRequiredCorsConfiguration(chain, true).getAllowedOrigins()).containsExactly("*");
assertThat(mapping.getRequiredCorsConfig().getAllowedOrigins()).containsExactly("*");
}
@Test
void actualRequestWithCorsConfigurationSource() throws Exception {
this.handlerMapping.setCorsConfigurationSource(new CustomCorsConfigurationSource());
this.request.setMethod(RequestMethod.GET.name());
this.request.setRequestURI("/foo");
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(this.request);
@PathPatternsParameterizedTest
void actualRequestWithCorsConfigSource(TestHandlerMapping mapping) throws Exception {
mapping.setCorsConfigurationSource(new CustomCorsConfigurationSource());
HandlerExecutionChain chain = mapping.getHandler(getCorsRequest("/foo"));
assertThat(chain).isNotNull();
assertThat(chain.getHandler()).isInstanceOf(SimpleHandler.class);
CorsConfiguration config = getRequiredCorsConfiguration(chain, false);
CorsConfiguration config = mapping.getRequiredCorsConfig();
assertThat(config).isNotNull();
assertThat(config.getAllowedOrigins()).containsExactly("*");
assertThat(config.getAllowCredentials()).isTrue();
}
@Test
void preflightRequestWithCorsConfigurationSource() throws Exception {
this.handlerMapping.setCorsConfigurationSource(new CustomCorsConfigurationSource());
this.request.setMethod(RequestMethod.OPTIONS.name());
this.request.setRequestURI("/foo");
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(this.request);
@PathPatternsParameterizedTest
void preflightRequestWithCorsConfigSource(TestHandlerMapping mapping) throws Exception {
mapping.setCorsConfigurationSource(new CustomCorsConfigurationSource());
HandlerExecutionChain chain = mapping.getHandler(getPreFlightRequest("/foo"));
assertThat(chain).isNotNull();
assertThat(chain.getHandler()).isNotNull();
assertThat(chain.getHandler().getClass().getSimpleName()).isEqualTo("PreFlightHandler");
CorsConfiguration config = getRequiredCorsConfiguration(chain, true);
CorsConfiguration config = mapping.getRequiredCorsConfig();
assertThat(config).isNotNull();
assertThat(config.getAllowedOrigins()).containsExactly("*");
assertThat(config.getAllowCredentials()).isTrue();
}
@SuppressWarnings("ConstantConditions")
private CorsConfiguration getRequiredCorsConfiguration(HandlerExecutionChain chain, boolean isPreFlightRequest) {
CorsConfiguration corsConfig = null;
if (isPreFlightRequest) {
Object handler = chain.getHandler();
assertThat(handler.getClass().getSimpleName()).isEqualTo("PreFlightHandler");
DirectFieldAccessor accessor = new DirectFieldAccessor(handler);
corsConfig = (CorsConfiguration) accessor.getPropertyValue("config");
private MockHttpServletRequest getCorsRequest(String requestURI) {
return createCorsRequest(HttpMethod.GET, requestURI);
}
private MockHttpServletRequest getPreFlightRequest(String requestURI) {
return createCorsRequest(HttpMethod.OPTIONS, requestURI);
}
private MockHttpServletRequest createCorsRequest(HttpMethod method, String requestURI) {
MockHttpServletRequest request = new MockHttpServletRequest(method.name(), requestURI);
request.setRemoteHost("domain1.com");
request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
return request;
}
private static class TestHandlerMapping extends AbstractHandlerMapping {
@Nullable
private CorsConfiguration savedCorsConfig;
TestHandlerMapping() {
this(null);
}
else {
HandlerInterceptor[] interceptors = chain.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
DirectFieldAccessor accessor = new DirectFieldAccessor(interceptors[0]);
corsConfig = (CorsConfiguration) accessor.getPropertyValue("config");
TestHandlerMapping(@Nullable PathPatternParser parser) {
setInterceptors(mock(HandlerInterceptor.class));
setApplicationContext(new StaticWebApplicationContext());
if (parser != null) {
setPatternParser(parser);
}
}
assertThat(corsConfig).isNotNull();
return corsConfig;
}
public class TestHandlerMapping extends AbstractHandlerMapping {
boolean hasSavedCorsConfig() {
return this.savedCorsConfig != null;
}
CorsConfiguration getRequiredCorsConfig() {
assertThat(this.savedCorsConfig).isNotNull();
return this.savedCorsConfig;
}
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
if (request.getRequestURI().equals("/cors")) {
protected Object getHandlerInternal(HttpServletRequest request) {
String lookupPath = initLookupPath(request);
if (lookupPath.equals("/cors")) {
return new CorsAwareHandler();
}
else if (request.getRequestURI().equals("/chain")) {
else if (lookupPath.equals("/chain")) {
return new HandlerExecutionChain(new CorsAwareHandler());
}
return new SimpleHandler();
}
@Override
protected String initLookupPath(HttpServletRequest request) {
// At runtime this is done by the DispatcherServlet
if (getPatternParser() != null) {
RequestPath requestPath = ServletRequestPathUtils.parseAndCache(request);
return requestPath.pathWithinApplication().value();
}
return super.initLookupPath(request);
}
@Override
protected HandlerExecutionChain getCorsHandlerExecutionChain(
HttpServletRequest request, HandlerExecutionChain chain, @Nullable CorsConfiguration config) {
this.savedCorsConfig = config;
return super.getCorsHandlerExecutionChain(request, chain, config);
}
}
public class SimpleHandler extends WebContentGenerator implements HttpRequestHandler {
private static class SimpleHandler extends WebContentGenerator implements HttpRequestHandler {
public SimpleHandler() {
SimpleHandler() {
super(METHOD_GET);
}
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
public void handleRequest(HttpServletRequest request, HttpServletResponse response) {
response.setStatus(HttpStatus.OK.value());
}
}
public class CorsAwareHandler extends SimpleHandler implements CorsConfigurationSource {
private static class CorsAwareHandler extends SimpleHandler implements CorsConfigurationSource {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
@@ -259,7 +266,7 @@ class CorsAbstractHandlerMappingTests {
}
public class CustomCorsConfigurationSource implements CorsConfigurationSource {
private static class CustomCorsConfigurationSource implements CorsConfigurationSource {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -23,8 +23,9 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
@@ -33,12 +34,16 @@ import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
@@ -53,85 +58,116 @@ import static org.springframework.web.servlet.HandlerMapping.BEST_MATCHING_PATTE
public class HandlerMappingIntrospectorTests {
@Test
public void detectHandlerMappings() throws Exception {
StaticWebApplicationContext cxt = new StaticWebApplicationContext();
cxt.registerSingleton("hmA", SimpleUrlHandlerMapping.class);
cxt.registerSingleton("hmB", SimpleUrlHandlerMapping.class);
cxt.registerSingleton("hmC", SimpleUrlHandlerMapping.class);
cxt.refresh();
void detectHandlerMappings() {
StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerSingleton("A", SimpleUrlHandlerMapping.class);
context.registerSingleton("B", SimpleUrlHandlerMapping.class);
context.registerSingleton("C", SimpleUrlHandlerMapping.class);
context.refresh();
List<?> expected = Arrays.asList(cxt.getBean("hmA"), cxt.getBean("hmB"), cxt.getBean("hmC"));
List<HandlerMapping> actual = getIntrospector(cxt).getHandlerMappings();
List<?> expected = Arrays.asList(context.getBean("A"), context.getBean("B"), context.getBean("C"));
List<HandlerMapping> actual = initIntrospector(context).getHandlerMappings();
assertThat(actual).isEqualTo(expected);
}
@Test
public void detectHandlerMappingsOrdered() throws Exception {
StaticWebApplicationContext cxt = new StaticWebApplicationContext();
MutablePropertyValues pvs = new MutablePropertyValues(Collections.singletonMap("order", "3"));
cxt.registerSingleton("hmA", SimpleUrlHandlerMapping.class, pvs);
pvs = new MutablePropertyValues(Collections.singletonMap("order", "2"));
cxt.registerSingleton("hmB", SimpleUrlHandlerMapping.class, pvs);
pvs = new MutablePropertyValues(Collections.singletonMap("order", "1"));
cxt.registerSingleton("hmC", SimpleUrlHandlerMapping.class, pvs);
cxt.refresh();
void detectHandlerMappingsOrdered() {
GenericWebApplicationContext context = new GenericWebApplicationContext();
context.registerBean("B", SimpleUrlHandlerMapping.class, () -> {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(2);
return mapping;
});
context.registerBean("C", SimpleUrlHandlerMapping.class, () -> {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(3);
return mapping;
});
context.registerBean("A", SimpleUrlHandlerMapping.class, () -> {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(1);
return mapping;
});
context.refresh();
List<?> expected = Arrays.asList(cxt.getBean("hmC"), cxt.getBean("hmB"), cxt.getBean("hmA"));
List<HandlerMapping> actual = getIntrospector(cxt).getHandlerMappings();
List<?> expected = Arrays.asList(context.getBean("A"), context.getBean("B"), context.getBean("C"));
List<HandlerMapping> actual = initIntrospector(context).getHandlerMappings();
assertThat(actual).isEqualTo(expected);
}
public void defaultHandlerMappings() throws Exception {
StaticWebApplicationContext cxt = new StaticWebApplicationContext();
cxt.refresh();
void defaultHandlerMappings() {
StaticWebApplicationContext context = new StaticWebApplicationContext();
context.refresh();
List<HandlerMapping> actual = initIntrospector(context).getHandlerMappings();
List<HandlerMapping> actual = getIntrospector(cxt).getHandlerMappings();
assertThat(actual.size()).isEqualTo(2);
assertThat(actual.get(0).getClass()).isEqualTo(BeanNameUrlHandlerMapping.class);
assertThat(actual.get(1).getClass()).isEqualTo(RequestMappingHandlerMapping.class);
}
@Test
public void getMatchable() throws Exception {
MutablePropertyValues pvs = new MutablePropertyValues(
Collections.singletonMap("urlMap", Collections.singletonMap("/path", new Object())));
@ParameterizedTest
@ValueSource(booleans = {true, false})
void getMatchable(boolean usePathPatterns) throws Exception {
StaticWebApplicationContext cxt = new StaticWebApplicationContext();
cxt.registerSingleton("hm", SimpleUrlHandlerMapping.class, pvs);
cxt.refresh();
PathPatternParser parser = new PathPatternParser();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/path");
MatchableHandlerMapping hm = getIntrospector(cxt).getMatchableHandlerMapping(request);
GenericWebApplicationContext context = new GenericWebApplicationContext();
context.registerBean("mapping", SimpleUrlHandlerMapping.class, () -> {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
if (usePathPatterns) {
mapping.setPatternParser(parser);
}
mapping.setUrlMap(Collections.singletonMap("/path/*", new Object()));
return mapping;
});
context.refresh();
assertThat(hm).isEqualTo(cxt.getBean("hm"));
assertThat(request.getAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE)).as("Attributes changes not ignored").isNull();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/path/123");
// Initialize the RequestPath. At runtime, ServletRequestPathFilter is expected to do that.
if (usePathPatterns) {
ServletRequestPathUtils.parseAndCache(request);
}
MatchableHandlerMapping mapping = initIntrospector(context).getMatchableHandlerMapping(request);
assertThat(mapping).isNotNull();
assertThat(mapping).isEqualTo(context.getBean("mapping"));
assertThat(request.getAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE)).as("Attribute changes not ignored").isNull();
String pattern = "/p*/*";
PathPattern pathPattern = parser.parse(pattern);
assertThat(usePathPatterns ? mapping.match(request, pathPattern) : mapping.match(request, pattern)).isNotNull();
pattern = "/b*/*";
pathPattern = parser.parse(pattern);
assertThat(usePathPatterns ? mapping.match(request, pathPattern) : mapping.match(request, pattern)).isNull();
}
@Test
public void getMatchableWhereHandlerMappingDoesNotImplementMatchableInterface() throws Exception {
void getMatchableWhereHandlerMappingDoesNotImplementMatchableInterface() {
StaticWebApplicationContext cxt = new StaticWebApplicationContext();
cxt.registerSingleton("hm1", TestHandlerMapping.class);
cxt.registerSingleton("mapping", TestHandlerMapping.class);
cxt.refresh();
MockHttpServletRequest request = new MockHttpServletRequest();
assertThatIllegalStateException().isThrownBy(() ->
getIntrospector(cxt).getMatchableHandlerMapping(request));
assertThatIllegalStateException().isThrownBy(() -> initIntrospector(cxt).getMatchableHandlerMapping(request));
}
@Test
public void getCorsConfigurationPreFlight() throws Exception {
AnnotationConfigWebApplicationContext cxt = new AnnotationConfigWebApplicationContext();
cxt.register(TestConfig.class);
cxt.refresh();
void getCorsConfigurationPreFlight() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(TestConfig.class);
context.refresh();
// PRE-FLIGHT
MockHttpServletRequest request = new MockHttpServletRequest("OPTIONS", "/path");
request.addHeader("Origin", "http://localhost:9000");
request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST");
CorsConfiguration corsConfig = getIntrospector(cxt).getCorsConfiguration(request);
CorsConfiguration corsConfig = initIntrospector(context).getCorsConfiguration(request);
assertThat(corsConfig).isNotNull();
assertThat(corsConfig.getAllowedOrigins()).isEqualTo(Collections.singletonList("http://localhost:9000"));
@@ -139,23 +175,23 @@ public class HandlerMappingIntrospectorTests {
}
@Test
public void getCorsConfigurationActual() throws Exception {
AnnotationConfigWebApplicationContext cxt = new AnnotationConfigWebApplicationContext();
cxt.register(TestConfig.class);
cxt.refresh();
void getCorsConfigurationActual() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(TestConfig.class);
context.refresh();
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/path");
request.addHeader("Origin", "http://localhost:9000");
CorsConfiguration corsConfig = getIntrospector(cxt).getCorsConfiguration(request);
CorsConfiguration corsConfig = initIntrospector(context).getCorsConfiguration(request);
assertThat(corsConfig).isNotNull();
assertThat(corsConfig.getAllowedOrigins()).isEqualTo(Collections.singletonList("http://localhost:9000"));
assertThat(corsConfig.getAllowedMethods()).isEqualTo(Collections.singletonList("POST"));
}
private HandlerMappingIntrospector getIntrospector(WebApplicationContext cxt) {
private HandlerMappingIntrospector initIntrospector(WebApplicationContext context) {
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector();
introspector.setApplicationContext(cxt);
introspector.setApplicationContext(context);
introspector.afterPropertiesSet();
return introspector;
}
@@ -164,14 +200,13 @@ public class HandlerMappingIntrospectorTests {
private static class TestHandlerMapping implements HandlerMapping {
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
public HandlerExecutionChain getHandler(HttpServletRequest request) {
return new HandlerExecutionChain(new Object());
}
}
@Configuration
@SuppressWarnings({"WeakerAccess", "unused"})
static class TestConfig {
@Bean
@@ -191,7 +226,7 @@ public class HandlerMappingIntrospectorTests {
private static class TestController {
@PostMapping("/path")
public void handle() {
void handle() {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -16,20 +16,18 @@
package org.springframework.web.servlet.handler;
import java.io.IOException;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.provider.Arguments;
import org.springframework.http.HttpStatus;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.support.WebContentGenerator;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import static org.assertj.core.api.Assertions.assertThat;
@@ -38,49 +36,51 @@ import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link org.springframework.web.servlet.HandlerMapping}.
* @author Brian Clozel
* @author Rossen Stoyanchev
*/
public class HandlerMappingTests {
class HandlerMappingTests {
private AbstractHandlerMapping handlerMapping = new TestHandlerMapping();
private StaticWebApplicationContext context = new StaticWebApplicationContext();
private MockHttpServletRequest request = new MockHttpServletRequest();
@SuppressWarnings("unused")
private static Stream<Arguments> pathPatternsArguments() {
List<Function<String, MockHttpServletRequest>> factories =
PathPatternsTestUtils.requestArguments().collect(Collectors.toList());
return Stream.of(
Arguments.arguments(new TestHandlerMapping(), factories.get(0)),
Arguments.arguments(new TestHandlerMapping(), factories.get(1))
);
}
@Test
public void orderedInterceptors() throws Exception {
HandlerInterceptor i1 = mock(HandlerInterceptor.class);
MappedInterceptor mappedInterceptor1 = new MappedInterceptor(new String[]{"/**"}, i1);
@PathPatternsParameterizedTest
void orderedInterceptors(
TestHandlerMapping mapping, Function<String, MockHttpServletRequest> requestFactory)
throws Exception {
MappedInterceptor i1 = new MappedInterceptor(new String[] {"/**"}, mock(HandlerInterceptor.class));
HandlerInterceptor i2 = mock(HandlerInterceptor.class);
HandlerInterceptor i3 = mock(HandlerInterceptor.class);
MappedInterceptor mappedInterceptor3 = new MappedInterceptor(new String[]{"/**"}, i3);
MappedInterceptor i3 = new MappedInterceptor(new String[] {"/**"}, mock(HandlerInterceptor.class));
HandlerInterceptor i4 = mock(HandlerInterceptor.class);
this.handlerMapping.setInterceptors(mappedInterceptor1, i2, mappedInterceptor3, i4);
this.handlerMapping.setApplicationContext(this.context);
HandlerExecutionChain chain = this.handlerMapping.getHandlerExecutionChain(new SimpleHandler(), this.request);
assertThat(chain.getInterceptors()).contains(
mappedInterceptor1.getInterceptor(), i2, mappedInterceptor3.getInterceptor(), i4);
mapping.setInterceptors(i1, i2, i3, i4);
mapping.setApplicationContext(new StaticWebApplicationContext());
HandlerExecutionChain chain = mapping.getHandler(requestFactory.apply("/"));
assertThat(chain).isNotNull();
assertThat(chain.getInterceptors()).contains(i1.getInterceptor(), i2, i3.getInterceptor(), i4);
}
class TestHandlerMapping extends AbstractHandlerMapping {
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
return new SimpleHandler();
}
}
private static class TestHandlerMapping extends AbstractHandlerMapping {
class SimpleHandler extends WebContentGenerator implements HttpRequestHandler {
public SimpleHandler() {
super(METHOD_GET);
TestHandlerMapping() {
}
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setStatus(HttpStatus.OK.value());
protected Object getHandlerInternal(HttpServletRequest request) {
initLookupPath(request);
return new Object();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -17,18 +17,19 @@ package org.springframework.web.servlet.handler;
import java.util.Comparator;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -37,96 +38,95 @@ import static org.mockito.Mockito.mock;
/**
* Test fixture for {@link MappedInterceptor} tests.
*
* @author Rossen Stoyanchev
*/
public class MappedInterceptorTests {
class MappedInterceptorTests {
private LocaleChangeInterceptor interceptor;
private static final LocaleChangeInterceptor delegate = new LocaleChangeInterceptor();
private final AntPathMatcher pathMatcher = new AntPathMatcher();
@BeforeEach
public void setup() {
this.interceptor = new LocaleChangeInterceptor();
@SuppressWarnings("unused")
private static Stream<Function<String, MockHttpServletRequest>> pathPatternsArguments() {
return PathPatternsTestUtils.requestArguments();
}
@PathPatternsParameterizedTest
void noPatterns(Function<String, MockHttpServletRequest> requestFactory) {
MappedInterceptor interceptor = new MappedInterceptor(null, null, delegate);
assertThat(interceptor.matches(requestFactory.apply("/foo"))).isTrue();
}
@PathPatternsParameterizedTest
void includePattern(Function<String, MockHttpServletRequest> requestFactory) {
MappedInterceptor interceptor = new MappedInterceptor(new String[] { "/foo/*" }, null, delegate);
assertThat(interceptor.matches(requestFactory.apply("/foo/bar"))).isTrue();
assertThat(interceptor.matches(requestFactory.apply("/bar/foo"))).isFalse();
}
@PathPatternsParameterizedTest
void includePatternWithMatrixVariables(Function<String, MockHttpServletRequest> requestFactory) {
MappedInterceptor interceptor = new MappedInterceptor(new String[] { "/foo*/*" }, null, delegate);
assertThat(interceptor.matches(requestFactory.apply("/foo;q=1/bar;s=2"))).isTrue();
}
@PathPatternsParameterizedTest
void excludePattern(Function<String, MockHttpServletRequest> requestFactory) {
MappedInterceptor interceptor = new MappedInterceptor(null, new String[] { "/admin/**" }, delegate);
assertThat(interceptor.matches(requestFactory.apply("/foo"))).isTrue();
assertThat(interceptor.matches(requestFactory.apply("/admin/foo"))).isFalse();
}
@PathPatternsParameterizedTest
void includeAndExcludePatterns(Function<String, MockHttpServletRequest> requestFactory) {
MappedInterceptor interceptor =
new MappedInterceptor(new String[] { "/**" }, new String[] { "/admin/**" }, delegate);
assertThat(interceptor.matches(requestFactory.apply("/foo"))).isTrue();
assertThat(interceptor.matches(requestFactory.apply("/admin/foo"))).isFalse();
}
@PathPatternsParameterizedTest
void customPathMatcher(Function<String, MockHttpServletRequest> requestFactory) {
MappedInterceptor interceptor = new MappedInterceptor(new String[] { "/foo/[0-9]*" }, null, delegate);
interceptor.setPathMatcher(new TestPathMatcher());
assertThat(interceptor.matches(requestFactory.apply("/foo/123"))).isTrue();
assertThat(interceptor.matches(requestFactory.apply("/foo/bar"))).isFalse();
}
@Test
public void noPatterns() {
MappedInterceptor mappedInterceptor = new MappedInterceptor(null, null, this.interceptor);
assertThat(mappedInterceptor.matches("/foo", pathMatcher)).isTrue();
void preHandle() throws Exception {
HandlerInterceptor delegate = mock(HandlerInterceptor.class);
new MappedInterceptor(null, delegate).preHandle(
mock(HttpServletRequest.class), mock(HttpServletResponse.class), null);
then(delegate).should().preHandle(any(HttpServletRequest.class), any(HttpServletResponse.class), any());
}
@Test
public void includePattern() {
MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[] { "/foo/*" }, this.interceptor);
void postHandle() throws Exception {
HandlerInterceptor delegate = mock(HandlerInterceptor.class);
assertThat(mappedInterceptor.matches("/foo/bar", pathMatcher)).isTrue();
assertThat(mappedInterceptor.matches("/bar/foo", pathMatcher)).isFalse();
new MappedInterceptor(null, delegate).postHandle(
mock(HttpServletRequest.class), mock(HttpServletResponse.class), null, mock(ModelAndView.class));
then(delegate).should().postHandle(any(), any(), any(), any());
}
@Test
public void includePatternWithMatrixVariables() {
MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[] { "/foo*/*" }, this.interceptor);
assertThat(mappedInterceptor.matches("/foo;q=1/bar;s=2", pathMatcher)).isTrue();
void afterCompletion() throws Exception {
HandlerInterceptor delegate = mock(HandlerInterceptor.class);
new MappedInterceptor(null, delegate).afterCompletion(
mock(HttpServletRequest.class), mock(HttpServletResponse.class), null, mock(Exception.class));
then(delegate).should().afterCompletion(any(), any(), any(), any());
}
@Test
public void excludePattern() {
MappedInterceptor mappedInterceptor = new MappedInterceptor(null, new String[] { "/admin/**" }, this.interceptor);
assertThat(mappedInterceptor.matches("/foo", pathMatcher)).isTrue();
assertThat(mappedInterceptor.matches("/admin/foo", pathMatcher)).isFalse();
}
@Test
public void includeAndExcludePatterns() {
MappedInterceptor mappedInterceptor = new MappedInterceptor(
new String[] { "/**" }, new String[] { "/admin/**" }, this.interceptor);
assertThat(mappedInterceptor.matches("/foo", pathMatcher)).isTrue();
assertThat(mappedInterceptor.matches("/admin/foo", pathMatcher)).isFalse();
}
@Test
public void customPathMatcher() {
MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[] { "/foo/[0-9]*" }, this.interceptor);
mappedInterceptor.setPathMatcher(new TestPathMatcher());
assertThat(mappedInterceptor.matches("/foo/123", pathMatcher)).isTrue();
assertThat(mappedInterceptor.matches("/foo/bar", pathMatcher)).isFalse();
}
@Test
public void preHandle() throws Exception {
HandlerInterceptor interceptor = mock(HandlerInterceptor.class);
MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[] { "/**" }, interceptor);
mappedInterceptor.preHandle(mock(HttpServletRequest.class), mock(HttpServletResponse.class), null);
then(interceptor).should().preHandle(any(HttpServletRequest.class), any(HttpServletResponse.class), any());
}
@Test
public void postHandle() throws Exception {
HandlerInterceptor interceptor = mock(HandlerInterceptor.class);
MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[] { "/**" }, interceptor);
mappedInterceptor.postHandle(mock(HttpServletRequest.class), mock(HttpServletResponse.class),
null, mock(ModelAndView.class));
then(interceptor).should().postHandle(any(), any(), any(), any());
}
@Test
public void afterCompletion() throws Exception {
HandlerInterceptor interceptor = mock(HandlerInterceptor.class);
MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[] { "/**" }, interceptor);
mappedInterceptor.afterCompletion(mock(HttpServletRequest.class), mock(HttpServletResponse.class),
null, mock(Exception.class));
then(interceptor).should().afterCompletion(any(), any(), any(), any());
}
public static class TestPathMatcher implements PathMatcher {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -16,16 +16,19 @@
package org.springframework.web.servlet.handler;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.stream.Stream;
import org.junit.jupiter.params.provider.Arguments;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockServletContext;
import org.springframework.web.util.ServletRequestPathUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -35,41 +38,49 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class PathMatchingUrlHandlerMappingTests {
public static final String CONF = "/org/springframework/web/servlet/handler/map3.xml";
@SuppressWarnings("unused")
static Stream<?> pathPatternsArguments() {
String location = "/org/springframework/web/servlet/handler/map3.xml";
WebApplicationContext wac = initConfig(location);
private HandlerMapping hm;
SimpleUrlHandlerMapping mapping1 = wac.getBean("urlMapping1", SimpleUrlHandlerMapping.class);
assertThat(mapping1.getPathPatternHandlerMap()).isNotEmpty();
private ConfigurableWebApplicationContext wac;
SimpleUrlHandlerMapping mapping2 = wac.getBean("urlMapping2", SimpleUrlHandlerMapping.class);
assertThat(mapping2.getPathPatternHandlerMap()).isEmpty();
@BeforeEach
public void setUp() throws Exception {
MockServletContext sc = new MockServletContext("");
wac = new XmlWebApplicationContext();
wac.setServletContext(sc);
wac.setConfigLocations(new String[] {CONF});
wac.refresh();
hm = (HandlerMapping) wac.getBean("urlMapping");
return Stream.of(Arguments.of(mapping1, wac), Arguments.of(mapping2, wac));
}
@Test
public void requestsWithHandlers() throws Exception {
private static WebApplicationContext initConfig(String... configLocations) {
MockServletContext sc = new MockServletContext("");
ConfigurableWebApplicationContext context = new XmlWebApplicationContext();
context.setServletContext(sc);
context.setConfigLocations(configLocations);
context.refresh();
return context;
}
@PathPatternsParameterizedTest
void requestsWithHandlers(HandlerMapping mapping, WebApplicationContext wac) throws Exception {
Object bean = wac.getBean("mainController");
MockHttpServletRequest req = new MockHttpServletRequest("GET", "/welcome.html");
HandlerExecutionChain hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
HandlerExecutionChain hec = getHandler(mapping, wac, req);
assertThat(hec.getHandler() == bean).isTrue();
req = new MockHttpServletRequest("GET", "/show.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
hec = getHandler(mapping, wac, req);
assertThat(hec.getHandler() == bean).isTrue();
req = new MockHttpServletRequest("GET", "/bookseats.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
hec = getHandler(mapping, wac, req);
assertThat(hec.getHandler() == bean).isTrue();
}
@Test
public void actualPathMatching() throws Exception {
@PathPatternsParameterizedTest
void actualPathMatching(SimpleUrlHandlerMapping mapping, WebApplicationContext wac) throws Exception {
// there a couple of mappings defined with which we can test the
// path matching, let's do that...
@@ -77,178 +88,199 @@ public class PathMatchingUrlHandlerMappingTests {
Object defaultBean = wac.getBean("starController");
// testing some normal behavior
MockHttpServletRequest req = new MockHttpServletRequest("GET", "/pathmatchingTest.html");
HandlerExecutionChain hec = getHandler(req);
assertThat(hec != null).as("Handler is null").isTrue();
assertThat(hec.getHandler() == bean).as("Handler is correct bean").isTrue();
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("/pathmatchingTest.html");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/pathmatchingTest.html");
HandlerExecutionChain chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
assertThat(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE))
.isEqualTo("/pathmatchingTest.html");
// no match, no forward slash included
req = new MockHttpServletRequest("GET", "welcome.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("welcome.html");
request = new MockHttpServletRequest("GET", "welcome.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
assertThat(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE))
.isEqualTo("welcome.html");
// testing some ????? behavior
req = new MockHttpServletRequest("GET", "/pathmatchingAA.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("pathmatchingAA.html");
request = new MockHttpServletRequest("GET", "/pathmatchingAA.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
assertThat(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE))
.isEqualTo("pathmatchingAA.html");
// testing some ????? behavior
req = new MockHttpServletRequest("GET", "/pathmatchingA.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("/pathmatchingA.html");
request = new MockHttpServletRequest("GET", "/pathmatchingA.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
assertThat(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE))
.isEqualTo("/pathmatchingA.html");
// testing some ????? behavior
req = new MockHttpServletRequest("GET", "/administrator/pathmatching.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/administrator/pathmatching.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
// testing simple /**/behavior
req = new MockHttpServletRequest("GET", "/administrator/test/pathmatching.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/administrator/test/pathmatching.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
// this should not match because of the administratorT
req = new MockHttpServletRequest("GET", "/administratort/pathmatching.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/administratort/pathmatching.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
// this should match because of *.jsp
req = new MockHttpServletRequest("GET", "/bla.jsp");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/bla.jsp");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
// should match because exact pattern is there
req = new MockHttpServletRequest("GET", "/administrator/another/bla.xml");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/administrator/another/bla.xml");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
// should not match, because there's not .gif extension in there
req = new MockHttpServletRequest("GET", "/administrator/another/bla.gif");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/administrator/another/bla.gif");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
// should match because there testlast* in there
req = new MockHttpServletRequest("GET", "/administrator/test/testlastbit");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/administrator/test/testlastbit");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
// but this not, because it's testlast and not testla
req = new MockHttpServletRequest("GET", "/administrator/test/testla");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/administrator/test/testla");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
req = new MockHttpServletRequest("GET", "/administrator/testing/longer/bla");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/administrator/testing/longer/bla");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/administrator/testing/longer/test.jsp");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/administrator/testing/longer/test.jsp");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/administrator/testing/longer2/notmatching/notmatching");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/administrator/testing/longer2/notmatching/notmatching");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
req = new MockHttpServletRequest("GET", "/shortpattern/testing/toolong");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/shortpattern/testing/toolong");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
req = new MockHttpServletRequest("GET", "/XXpathXXmatching.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/XXpathXXmatching.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/pathXXmatching.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/pathXXmatching.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/XpathXXmatching.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/XpathXXmatching.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
req = new MockHttpServletRequest("GET", "/XXpathmatching.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/XXpathmatching.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
req = new MockHttpServletRequest("GET", "/show12.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/show12.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/show123.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/show123.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/show1.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/show1.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/reallyGood-test-is-this.jpeg");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/reallyGood-test-is-this.jpeg");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/reallyGood-tst-is-this.jpeg");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/reallyGood-tst-is-this.jpeg");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
req = new MockHttpServletRequest("GET", "/testing/test.jpeg");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/testing/test.jpeg");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/testing/test.jpg");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/testing/test.jpg");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
req = new MockHttpServletRequest("GET", "/anotherTest");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/anotherTest");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/stillAnotherTest");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/stillAnotherTest");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
// there outofpattern*yeah in the pattern, so this should fail
req = new MockHttpServletRequest("GET", "/outofpattern*ye");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/outofpattern*ye");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
req = new MockHttpServletRequest("GET", "/test't est/path'm atching.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/test't%20est/path'm%20atching.html");
chain = getHandler(mapping, wac, request);
assertThat(chain.getHandler()).isSameAs(defaultBean);
req = new MockHttpServletRequest("GET", "/test%26t%20est/path%26m%20atching.html");
hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
request = new MockHttpServletRequest("GET", "/test%26t%20est/path%26m%20atching.html");
chain = getHandler(mapping, wac, request);
if (!mapping.getPathPatternHandlerMap().isEmpty()) {
assertThat(chain.getHandler())
.as("PathPattern always matches to encoded paths.")
.isSameAs(bean);
}
else {
assertThat(chain.getHandler())
.as("PathMatcher should not match encoded pattern with urlDecode=true")
.isSameAs(defaultBean);
}
}
@Test
public void defaultMapping() throws Exception {
@PathPatternsParameterizedTest
void defaultMapping(HandlerMapping mapping, WebApplicationContext wac) throws Exception {
Object bean = wac.getBean("starController");
MockHttpServletRequest req = new MockHttpServletRequest("GET", "/goggog.html");
HandlerExecutionChain hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
HandlerExecutionChain hec = getHandler(mapping, wac, req);
assertThat(hec.getHandler()).isSameAs(bean);
}
@Test
public void mappingExposedInRequest() throws Exception {
@PathPatternsParameterizedTest
void mappingExposedInRequest(HandlerMapping mapping, WebApplicationContext wac) throws Exception {
Object bean = wac.getBean("mainController");
MockHttpServletRequest req = new MockHttpServletRequest("GET", "/show.html");
HandlerExecutionChain hec = getHandler(req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).as("Mapping not exposed").isEqualTo("show.html");
HandlerExecutionChain hec = getHandler(mapping, wac, req);
assertThat(hec.getHandler()).isSameAs(bean);
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE))
.as("Mapping not exposed").isEqualTo("show.html");
}
private HandlerExecutionChain getHandler(MockHttpServletRequest req) throws Exception {
HandlerExecutionChain hec = hm.getHandler(req);
HandlerInterceptor[] interceptors = hec.getInterceptors();
private HandlerExecutionChain getHandler(
HandlerMapping mapping, WebApplicationContext wac, MockHttpServletRequest request)
throws Exception {
// At runtime this is done by the DispatcherServlet
if (((AbstractHandlerMapping) mapping).getPatternParser() != null) {
ServletRequestPathUtils.parseAndCache(request);
}
HandlerExecutionChain executionChain = mapping.getHandler(request);
HandlerInterceptor[] interceptors = executionChain.getInterceptors();
if (interceptors != null) {
for (HandlerInterceptor interceptor : interceptors) {
interceptor.preHandle(req, null, hec.getHandler());
interceptor.preHandle(request, null, executionChain.getHandler());
}
}
return hec;
return executionChain;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2002-2020 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
*
* https://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.handler;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation for tests parameterized to use either
* {@link org.springframework.web.util.pattern.PathPatternParser} or
* {@link org.springframework.util.PathMatcher} for URL pattern matching.
*
* @author Rossen Stoyanchev
* @since 5.3
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@org.junit.jupiter.params.ParameterizedTest
@org.junit.jupiter.params.provider.MethodSource("pathPatternsArguments")
public @interface PathPatternsParameterizedTest {
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2002-2020 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
*
* https://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.handler;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import org.springframework.lang.Nullable;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.UrlPathHelper;
/**
* Utility methods to help with parameterized tests for URL pattern matching
* via pre-parsed {@code PathPattern}s or String pattern matching with
* {@code PathMatcher}.
*
* @author Rossen Stoyanchev
* @since 5.3
*/
public abstract class PathPatternsTestUtils {
public static Stream<Function<String, MockHttpServletRequest>> requestArguments() {
return requestArguments(null);
}
public static Stream<Function<String, MockHttpServletRequest>> requestArguments(@Nullable String contextPath) {
return Stream.of(
path -> {
MockHttpServletRequest request = createRequest("GET", contextPath, path);
ServletRequestPathUtils.parseAndCache(request);
return request;
},
path -> {
MockHttpServletRequest request = createRequest("GET", contextPath, path);
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
return request;
}
);
}
/**
* Create a MockHttpServletRequest and initialize the request attributes for
* the lookupPath via {@link ServletRequestPathUtils} or {@link UrlPathHelper}
* depending on the setting of the parsedPatterns argument.
* <p>At runtime this would be done by the DispatcherServlet (for the RequestPath)
* and by the AbstractHandlerMapping (for UrlPathHelper).
*/
public static MockHttpServletRequest initRequest(String method, String requestUri, boolean parsedPatterns) {
return initRequest(method, null, requestUri, parsedPatterns);
}
/**
* See {@link #initRequest(String, String, boolean)}. This variant adds a contextPath.
*/
public static MockHttpServletRequest initRequest(
String method, @Nullable String contextPath, String path, boolean parsedPatterns) {
return initRequest(method, contextPath, path, parsedPatterns, null);
}
/**
* See {@link #initRequest(String, String, boolean)}. This variant adds a contextPath
* and a post-construct initializer to apply further changes before the
* lookupPath is resolved.
*/
public static MockHttpServletRequest initRequest(
String method, @Nullable String contextPath, String path,
boolean parsedPatterns, @Nullable Consumer<MockHttpServletRequest> postConstructInitializer) {
MockHttpServletRequest request = createRequest(method, contextPath, path);
if (postConstructInitializer != null) {
postConstructInitializer.accept(request);
}
// At runtime this is done by the DispatcherServlet and AbstractHandlerMapping
if (parsedPatterns) {
ServletRequestPathUtils.parseAndCache(request);
}
else {
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
}
return request;
}
private static MockHttpServletRequest createRequest(String method, @Nullable String contextPath, String path) {
if (contextPath != null) {
String requestUri = contextPath + (path.startsWith("/") ? "" : "/") + path;
MockHttpServletRequest request = new MockHttpServletRequest(method, requestUri);
request.setContextPath(contextPath);
return request;
}
else {
return new MockHttpServletRequest(method, path);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -19,6 +19,8 @@ package org.springframework.web.servlet.handler;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -33,6 +35,8 @@ import org.springframework.web.util.WebUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.springframework.web.servlet.HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE;
import static org.springframework.web.servlet.HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE;
/**
* @author Rod Johnson
@@ -48,44 +52,38 @@ public class SimpleUrlHandlerMappingTests {
root.setServletContext(sc);
root.setConfigLocations("/org/springframework/web/servlet/handler/map1.xml");
root.refresh();
XmlWebApplicationContext wac = new XmlWebApplicationContext();
wac.setParent(root);
wac.setServletContext(sc);
wac.setNamespace("map2err");
wac.setConfigLocations("/org/springframework/web/servlet/handler/map2err.xml");
assertThatExceptionOfType(FatalBeanException.class).isThrownBy(
wac::refresh)
.withCauseInstanceOf(NoSuchBeanDefinitionException.class)
.satisfies(ex -> assertThat(((NoSuchBeanDefinitionException) ex.getCause()).getBeanName()).isEqualTo("mainControlle"));
}
@Test
public void urlMappingWithUrlMap() throws Exception {
checkMappings("urlMapping");
}
@Test
public void urlMappingWithProps() throws Exception {
checkMappings("urlMappingWithProps");
assertThatExceptionOfType(FatalBeanException.class)
.isThrownBy(wac::refresh)
.withCauseInstanceOf(NoSuchBeanDefinitionException.class)
.satisfies(ex -> {
NoSuchBeanDefinitionException cause = (NoSuchBeanDefinitionException) ex.getCause();
assertThat(cause.getBeanName()).isEqualTo("mainControlle");
});
}
@Test
public void testNewlineInRequest() throws Exception {
Object controller = new Object();
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping(
Collections.singletonMap("/*/baz", controller));
handlerMapping.setUrlDecode(false);
handlerMapping.setApplicationContext(new StaticApplicationContext());
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(Collections.singletonMap("/*/baz", controller));
mapping.setUrlDecode(false);
mapping.setApplicationContext(new StaticApplicationContext());
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo%0a%0dbar/baz");
HandlerExecutionChain hec = handlerMapping.getHandler(request);
HandlerExecutionChain hec = mapping.getHandler(request);
assertThat(hec).isNotNull();
assertThat(hec.getHandler()).isSameAs(controller);
}
@SuppressWarnings("resource")
private void checkMappings(String beanName) throws Exception {
@ParameterizedTest
@ValueSource(strings = {"urlMapping", "urlMappingWithProps", "urlMappingWithPathPatterns"})
void checkMappings(String beanName) throws Exception {
MockServletContext sc = new MockServletContext("");
XmlWebApplicationContext wac = new XmlWebApplicationContext();
wac.setServletContext(sc);
@@ -96,76 +94,76 @@ public class SimpleUrlHandlerMappingTests {
Object defaultBean = wac.getBean("starController");
HandlerMapping hm = (HandlerMapping) wac.getBean(beanName);
MockHttpServletRequest req = new MockHttpServletRequest("GET", "/welcome.html");
HandlerExecutionChain hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("/welcome.html");
assertThat(req.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE)).isEqualTo(bean);
boolean usePathPatterns = (((AbstractHandlerMapping) hm).getPatternParser() != null);
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("GET", "/welcome.html", usePathPatterns);
HandlerExecutionChain chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(bean);
assertThat(request.getAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("/welcome.html");
assertThat(request.getAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE)).isEqualTo(bean);
req = new MockHttpServletRequest("GET", "/welcome.x");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == otherBean).as("Handler is correct bean").isTrue();
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("welcome.x");
assertThat(req.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE)).isEqualTo(otherBean);
request = PathPatternsTestUtils.initRequest("GET", "/welcome.x", usePathPatterns);
chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(otherBean);
assertThat(request.getAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("welcome.x");
assertThat(request.getAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE)).isEqualTo(otherBean);
req = new MockHttpServletRequest("GET", "/welcome/");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == otherBean).as("Handler is correct bean").isTrue();
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("welcome");
request = PathPatternsTestUtils.initRequest("GET", "/welcome/", usePathPatterns);
chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(otherBean);
assertThat(request.getAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("welcome");
req = new MockHttpServletRequest("GET", "/");
req.setServletPath("/welcome.html");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = PathPatternsTestUtils.initRequest("GET", "/", usePathPatterns);
request.setServletPath("/welcome.html");
chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/welcome.html");
req.setContextPath("/app");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = PathPatternsTestUtils.initRequest("GET", "/app", "/welcome.html", usePathPatterns);
chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/show.html");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = PathPatternsTestUtils.initRequest("GET", "/show.html", usePathPatterns);
chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/bookseats.html");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = PathPatternsTestUtils.initRequest("GET", "/bookseats.html", usePathPatterns);
chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/original-welcome.html");
req.setAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE, "/welcome.html");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = PathPatternsTestUtils.initRequest("GET", null, "/original-welcome.html", usePathPatterns,
req -> req.setAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE, "/welcome.html"));
chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/original-show.html");
req.setAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE, "/show.html");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = PathPatternsTestUtils.initRequest("GET", null, "/original-show.html", usePathPatterns,
req -> req.setAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE, "/show.html"));
chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/original-bookseats.html");
req.setAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE, "/bookseats.html");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
request = PathPatternsTestUtils.initRequest("GET", null, "/original-bookseats.html", usePathPatterns,
req -> req.setAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE, "/bookseats.html"));
chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(bean);
req = new MockHttpServletRequest("GET", "/");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == bean).as("Handler is correct bean").isTrue();
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("/");
request = PathPatternsTestUtils.initRequest("GET", "/", usePathPatterns);
chain = getHandler(hm, request);
assertThat(chain.getHandler()).isSameAs(bean);
assertThat(request.getAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("/");
req = new MockHttpServletRequest("GET", "/somePath");
hec = getHandler(hm, req);
assertThat(hec != null && hec.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
assertThat(req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("/somePath");
request = PathPatternsTestUtils.initRequest("GET", "/somePath", usePathPatterns);
chain = getHandler(hm, request);
assertThat(chain.getHandler() == defaultBean).as("Handler is correct bean").isTrue();
assertThat(request.getAttribute(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).isEqualTo("/somePath");
}
private HandlerExecutionChain getHandler(HandlerMapping hm, MockHttpServletRequest req) throws Exception {
HandlerExecutionChain hec = hm.getHandler(req);
HandlerInterceptor[] interceptors = hec.getInterceptors();
private HandlerExecutionChain getHandler(HandlerMapping mapping, MockHttpServletRequest request) throws Exception {
HandlerExecutionChain chain = mapping.getHandler(request);
HandlerInterceptor[] interceptors = chain.getInterceptors();
if (interceptors != null) {
for (HandlerInterceptor interceptor : interceptors) {
interceptor.preHandle(req, null, hec.getHandler());
interceptor.preHandle(request, null, chain.getHandler());
}
}
return hec;
return chain;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -16,16 +16,21 @@
package org.springframework.web.servlet.mvc;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.springframework.ui.ModelMap;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import org.springframework.web.util.ServletRequestPathUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -34,162 +39,164 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Rick Evans
* @since 14.09.2005
*/
public class UrlFilenameViewControllerTests {
class UrlFilenameViewControllerTests {
private PathMatcher pathMatcher = new AntPathMatcher();
@SuppressWarnings("unused")
private static Stream<Function<String, MockHttpServletRequest>> pathPatternsArguments() {
return PathPatternsTestUtils.requestArguments();
}
@Test
public void withPlainFilename() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/index");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
@PathPatternsParameterizedTest
void withPlainFilename(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
MockHttpServletRequest request = requestFactory.apply("/index");
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("index");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void withFilenamePlusExtension() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/index.html");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
@PathPatternsParameterizedTest
void withFilenamePlusExtension(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
MockHttpServletRequest request = requestFactory.apply("/index.html");
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("index");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void withFilenameAndMatrixVariables() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/index;a=A;b=B");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
@PathPatternsParameterizedTest
void withFilenameAndMatrixVariables(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
MockHttpServletRequest request = requestFactory.apply("/index;a=A;b=B");
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("index");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void withPrefixAndSuffix() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
ctrl.setPrefix("mypre_");
ctrl.setSuffix("_mysuf");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/index.html");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
@PathPatternsParameterizedTest
void withPrefixAndSuffix(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
controller.setPrefix("mypre_");
controller.setSuffix("_mysuf");
MockHttpServletRequest request = requestFactory.apply("/index.html");
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("mypre_index_mysuf");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void withPrefix() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
ctrl.setPrefix("mypre_");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/index.html");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
@PathPatternsParameterizedTest
void withPrefix(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
controller.setPrefix("mypre_");
MockHttpServletRequest request = requestFactory.apply("/index.html");
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("mypre_index");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void withSuffix() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
ctrl.setSuffix("_mysuf");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/index.html");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
@PathPatternsParameterizedTest
void withSuffix(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
controller.setSuffix("_mysuf");
MockHttpServletRequest request = requestFactory.apply("/index.html");
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("index_mysuf");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void multiLevel() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/docs/cvs/commit.html");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
@PathPatternsParameterizedTest
void multiLevel(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
MockHttpServletRequest request = requestFactory.apply("/docs/cvs/commit.html");
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("docs/cvs/commit");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void multiLevelWithMapping() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/docs/cvs/commit.html");
@PathPatternsParameterizedTest
void multiLevelWithMapping(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
MockHttpServletRequest request = requestFactory.apply("/docs/cvs/commit.html");
exposePathInMapping(request, "/docs/**");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("cvs/commit");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void multiLevelMappingWithFallback() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/docs/cvs/commit.html");
@PathPatternsParameterizedTest
void multiLevelMappingWithFallback(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
MockHttpServletRequest request = requestFactory.apply("/docs/cvs/commit.html");
exposePathInMapping(request, "/docs/cvs/commit.html");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("docs/cvs/commit");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void withContextMapping() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
@PathPatternsParameterizedTest
void withContextMapping(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myapp/docs/cvs/commit.html");
request.setContextPath("/myapp");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
ServletRequestPathUtils.parseAndCache(request);
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("docs/cvs/commit");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void settingPrefixToNullCausesEmptyStringToBeUsed() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
ctrl.setPrefix(null);
assertThat(ctrl.getPrefix()).as("For setPrefix(..) with null, the empty string must be used instead.").isNotNull();
assertThat(ctrl.getPrefix()).as("For setPrefix(..) with null, the empty string must be used instead.").isEqualTo("");
void settingPrefixToNullCausesEmptyStringToBeUsed() {
UrlFilenameViewController controller = new UrlFilenameViewController();
controller.setPrefix(null);
assertThat(controller.getPrefix())
.as("For setPrefix(..) with null, the empty string must be used instead.")
.isNotNull();
assertThat(controller.getPrefix())
.as("For setPrefix(..) with null, the empty string must be used instead.")
.isEqualTo("");
}
@Test
public void settingSuffixToNullCausesEmptyStringToBeUsed() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
ctrl.setSuffix(null);
assertThat(ctrl.getSuffix()).as("For setPrefix(..) with null, the empty string must be used instead.").isNotNull();
assertThat(ctrl.getSuffix()).as("For setPrefix(..) with null, the empty string must be used instead.").isEqualTo("");
void settingSuffixToNullCausesEmptyStringToBeUsed() {
UrlFilenameViewController controller = new UrlFilenameViewController();
controller.setSuffix(null);
assertThat(controller.getSuffix())
.as("For setPrefix(..) with null, the empty string must be used instead.")
.isNotNull();
assertThat(controller.getSuffix())
.as("For setPrefix(..) with null, the empty string must be used instead.")
.isEqualTo("");
}
/**
* This is the expected behavior, and it now has a test to prove it.
* https://opensource.atlassian.com/projects/spring/browse/SPR-2789
*/
@Test
public void nestedPathisUsedAsViewName_InBreakingChangeFromSpring12Line() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/products/view.html");
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
@PathPatternsParameterizedTest
void nestedPathisUsedAsViewName_InBreakingChangeFromSpring12Line(
Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
MockHttpServletRequest request = requestFactory.apply("/products/view.html");
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("products/view");
assertThat(mv.getModel().isEmpty()).isTrue();
}
@Test
public void withFlashAttributes() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/index");
@PathPatternsParameterizedTest
void withFlashAttributes(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
UrlFilenameViewController controller = new UrlFilenameViewController();
MockHttpServletRequest request = requestFactory.apply("/index");
request.setAttribute(DispatcherServlet.INPUT_FLASH_MAP_ATTRIBUTE, new ModelMap("name", "value"));
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
ModelAndView mv = controller.handleRequest(request, new MockHttpServletResponse());
assertThat(mv.getViewName()).isEqualTo("index");
assertThat(mv.getModel().size()).isEqualTo(1);
assertThat(mv.getModel().get("name")).isEqualTo("value");
}
private void exposePathInMapping(MockHttpServletRequest request, String mapping) {
String pathInMapping = this.pathMatcher.extractPathWithinPattern(mapping, request.getRequestURI());
String pathInMapping = new AntPathMatcher().extractPathWithinPattern(mapping, request.getRequestURI());
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathInMapping);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -17,80 +17,83 @@
package org.springframework.web.servlet.mvc;
import java.util.Properties;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Unit tests for {@link WebContentInterceptor}.
* @author Rick Evans
* @author Brian Clozel
* @author Rossen Stoyanchev
*/
public class WebContentInterceptorTests {
class WebContentInterceptorTests {
private MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
private final MockHttpServletResponse response = new MockHttpServletResponse();
private MockHttpServletResponse response = new MockHttpServletResponse();
private final WebContentInterceptor interceptor = new WebContentInterceptor();
private final Object handler = new Object();
@Test
public void cacheResourcesConfiguration() throws Exception {
WebContentInterceptor interceptor = new WebContentInterceptor();
@SuppressWarnings("unused")
private static Stream<Function<String, MockHttpServletRequest>> pathPatternsArguments() {
return PathPatternsTestUtils.requestArguments();
}
@PathPatternsParameterizedTest
void cacheResourcesConfiguration(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
interceptor.setCacheSeconds(10);
interceptor.preHandle(request, response, null);
interceptor.preHandle(requestFactory.apply("/"), response, handler);
Iterable<String> cacheControlHeaders = response.getHeaders("Cache-Control");
assertThat(cacheControlHeaders).contains("max-age=10");
}
@Test
public void mappedCacheConfigurationOverridesGlobal() throws Exception {
@PathPatternsParameterizedTest
void mappedCacheConfigurationOverridesGlobal(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
Properties mappings = new Properties();
mappings.setProperty("*/*handle.vm", "-1"); // was **/*handle.vm
mappings.setProperty("/*/*handle.vm", "-1");
WebContentInterceptor interceptor = new WebContentInterceptor();
interceptor.setCacheSeconds(10);
interceptor.setCacheMappings(mappings);
// request.setRequestURI("http://localhost:7070/example/adminhandle.vm");
request.setRequestURI("example/adminhandle.vm");
interceptor.preHandle(request, response, null);
MockHttpServletRequest request = requestFactory.apply("/example/adminhandle.vm");
interceptor.preHandle(request, response, handler);
Iterable<String> cacheControlHeaders = response.getHeaders("Cache-Control");
assertThat(cacheControlHeaders).isEmpty();
// request.setRequestURI("http://localhost:7070/example/bingo.html");
request.setRequestURI("example/bingo.html");
interceptor.preHandle(request, response, null);
request = requestFactory.apply("/example/bingo.html");
interceptor.preHandle(request, response, handler);
cacheControlHeaders = response.getHeaders("Cache-Control");
assertThat(cacheControlHeaders).contains("max-age=10");
}
@Test
public void preventCacheConfiguration() throws Exception {
WebContentInterceptor interceptor = new WebContentInterceptor();
@PathPatternsParameterizedTest
void preventCacheConfiguration(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
interceptor.setCacheSeconds(0);
interceptor.preHandle(request, response, null);
interceptor.preHandle(requestFactory.apply("/"), response, handler);
Iterable<String> cacheControlHeaders = response.getHeaders("Cache-Control");
assertThat(cacheControlHeaders).contains("no-store");
}
@Test
public void emptyCacheConfiguration() throws Exception {
WebContentInterceptor interceptor = new WebContentInterceptor();
@PathPatternsParameterizedTest
void emptyCacheConfiguration(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
interceptor.setCacheSeconds(-1);
interceptor.preHandle(request, response, null);
interceptor.preHandle(requestFactory.apply("/"), response, handler);
Iterable<String> expiresHeaders = response.getHeaders("Expires");
assertThat(expiresHeaders).isEmpty();
@@ -98,50 +101,45 @@ public class WebContentInterceptorTests {
assertThat(cacheControlHeaders).isEmpty();
}
// SPR-13252, SPR-14053
@Test
public void cachingConfigAndPragmaHeader() throws Exception {
WebContentInterceptor interceptor = new WebContentInterceptor();
interceptor.setCacheSeconds(10);
@PathPatternsParameterizedTest // SPR-13252, SPR-14053
void cachingConfigAndPragmaHeader(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
interceptor.preHandle(request, response, null);
interceptor.setCacheSeconds(10);
interceptor.preHandle(requestFactory.apply("/"), response, handler);
assertThat(response.getHeader("Pragma")).isEqualTo("");
assertThat(response.getHeader("Expires")).isEqualTo("");
}
// SPR-13252, SPR-14053
@SuppressWarnings("deprecation")
@Test
public void http10CachingConfigAndPragmaHeader() throws Exception {
WebContentInterceptor interceptor = new WebContentInterceptor();
@PathPatternsParameterizedTest // SPR-13252, SPR-14053
void http10CachingConfigAndPragmaHeader(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
interceptor.setCacheSeconds(10);
interceptor.setAlwaysMustRevalidate(true);
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
interceptor.preHandle(request, response, null);
interceptor.preHandle(requestFactory.apply("/"), response, handler);
assertThat(response.getHeader("Pragma")).isEqualTo("");
assertThat(response.getHeader("Expires")).isEqualTo("");
}
@SuppressWarnings("deprecation")
@Test
public void http10CachingConfigAndSpecificMapping() throws Exception {
WebContentInterceptor interceptor = new WebContentInterceptor();
@PathPatternsParameterizedTest
void http10CachingConfigAndSpecificMapping(Function<String, MockHttpServletRequest> requestFactory) throws Exception {
interceptor.setCacheSeconds(0);
interceptor.setUseExpiresHeader(true);
interceptor.setAlwaysMustRevalidate(true);
Properties mappings = new Properties();
mappings.setProperty("*/*.cache.html", "10"); // was **/*.cache.html
mappings.setProperty("/*/*.cache.html", "10");
interceptor.setCacheMappings(mappings);
// request.setRequestURI("https://example.org/foo/page.html");
request.setRequestURI("foo/page.html");
interceptor.preHandle(request, response, null);
MockHttpServletRequest request = requestFactory.apply("/foo/page.html");
MockHttpServletResponse response = new MockHttpServletResponse();
interceptor.preHandle(request, response, handler);
Iterable<String> expiresHeaders = response.getHeaders("Expires");
assertThat(expiresHeaders).hasSize(1);
@@ -150,10 +148,9 @@ public class WebContentInterceptorTests {
Iterable<String> pragmaHeaders = response.getHeaders("Pragma");
assertThat(pragmaHeaders).containsExactly("no-cache");
// request.setRequestURI("https://example.org/page.cache.html");
request = new MockHttpServletRequest("GET", "foo/page.cache.html");
request = requestFactory.apply("/foo/page.cache.html");
response = new MockHttpServletResponse();
interceptor.preHandle(request, response, null);
interceptor.preHandle(request, response, handler);
expiresHeaders = response.getHeaders("Expires");
assertThat(expiresHeaders).hasSize(1);
@@ -162,10 +159,9 @@ public class WebContentInterceptorTests {
}
@Test
public void throwsExceptionWithNullPathMatcher() throws Exception {
WebContentInterceptor interceptor = new WebContentInterceptor();
assertThatIllegalArgumentException().isThrownBy(() ->
interceptor.setPathMatcher(null));
void throwsExceptionWithNullPathMatcher() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new WebContentInterceptor().setPathMatcher(null));
}
}

View File

@@ -0,0 +1,206 @@
/*
* Copyright 2002-2020 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
*
* https://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.mvc.condition;
import javax.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit tests for {@link PathPatternsRequestCondition}.
* @author Rossen Stoyanchev
*/
public class PathPatternsRequestConditionTests {
private static final PathPatternParser parser = new PathPatternParser();
@Test
void prependSlash() {
assertThat(createCondition("foo").getPatternValues().iterator().next())
.isEqualTo("/foo");
}
@Test
void prependNonEmptyPatternsOnly() {
assertThat(createCondition("").getPatternValues().iterator().next())
.as("Do not prepend empty patterns (SPR-8255)")
.isEqualTo("");
}
@Test
void combineEmptySets() {
PathPatternsRequestCondition c1 = createCondition();
PathPatternsRequestCondition c2 = createCondition();
PathPatternsRequestCondition c3 = c1.combine(c2);
assertThat(c3).isSameAs(c1);
assertThat(c1.getPatternValues()).isSameAs(c2.getPatternValues()).containsExactly("");
}
@Test
void combineOnePatternWithEmptySet() {
PathPatternsRequestCondition c1 = createCondition("/type1", "/type2");
PathPatternsRequestCondition c2 = createCondition();
assertThat(c1.combine(c2)).isEqualTo(createCondition("/type1", "/type2"));
c1 = createCondition();
c2 = createCondition("/method1", "/method2");
assertThat(c1.combine(c2)).isEqualTo(createCondition("/method1", "/method2"));
}
@Test
void combineMultiplePatterns() {
PathPatternsRequestCondition c1 = createCondition("/t1", "/t2");
PathPatternsRequestCondition c2 = createCondition("/m1", "/m2");
assertThat(c1.combine(c2)).isEqualTo(createCondition("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2"));
}
@Test
void matchDirectPath() {
MockHttpServletRequest request = createRequest("/foo");
PathPatternsRequestCondition condition = createCondition("/foo");
PathPatternsRequestCondition match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
}
@Test
void matchPattern() {
MockHttpServletRequest request = createRequest("/foo/bar");
PathPatternsRequestCondition condition = createCondition("/foo/*");
PathPatternsRequestCondition match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
}
@Test
void matchPatternWithContextPath() {
MockHttpServletRequest request = createRequest("/app", "/app/foo/bar");
PathPatternsRequestCondition condition = createCondition("/foo/*");
PathPatternsRequestCondition match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
}
@Test
void matchSortPatterns() {
MockHttpServletRequest request = createRequest("/foo/bar");
PathPatternsRequestCondition condition = createCondition("/**", "/foo/bar", "/foo/*");
PathPatternsRequestCondition match = condition.getMatchingCondition(request);
PathPatternsRequestCondition expected = createCondition("/foo/bar", "/foo/*", "/**");
assertThat(match).isEqualTo(expected);
}
@Test
void matchTrailingSlash() {
MockHttpServletRequest request = createRequest("/foo/");
PathPatternsRequestCondition condition = createCondition("/foo");
PathPatternsRequestCondition match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
assertThat(match.getPatternValues().iterator().next()).as("Should match by default").isEqualTo("/foo");
PathPatternParser strictParser = new PathPatternParser();
strictParser.setMatchOptionalTrailingSeparator(false);
condition = new PathPatternsRequestCondition(strictParser, "/foo");
match = condition.getMatchingCondition(request);
assertThat(match).isNull();
}
@Test
void matchPatternContainsExtension() {
MockHttpServletRequest request = createRequest("/foo.html");
PathPatternsRequestCondition match = createCondition("/foo.jpg").getMatchingCondition(request);
assertThat(match).isNull();
}
@Test // gh-22543
void matchWithEmptyPatterns() {
PathPatternsRequestCondition condition = createCondition();
assertThat(condition.getMatchingCondition(createRequest(""))).isNotNull();
assertThat(condition.getMatchingCondition(createRequest("/anything"))).isNull();
condition = condition.combine(createCondition());
assertThat(condition.getMatchingCondition(createRequest(""))).isNotNull();
assertThat(condition.getMatchingCondition(createRequest("/anything"))).isNull();
}
@Test
void compareEqualPatterns() {
PathPatternsRequestCondition c1 = createCondition("/foo*");
PathPatternsRequestCondition c2 = createCondition("/foo*");
assertThat(c1.compareTo(c2, createRequest("/foo"))).isEqualTo(0);
}
@Test
void comparePatternSpecificity() {
PathPatternsRequestCondition c1 = createCondition("/fo*");
PathPatternsRequestCondition c2 = createCondition("/foo");
assertThat(c1.compareTo(c2, createRequest("/foo"))).isEqualTo(1);
}
@Test
void compareNumberOfMatchingPatterns() {
HttpServletRequest request = createRequest("/foo");
PathPatternsRequestCondition c1 = createCondition("/foo", "/bar");
PathPatternsRequestCondition c2 = createCondition("/foo", "/f*");
PathPatternsRequestCondition match1 = c1.getMatchingCondition(request);
PathPatternsRequestCondition match2 = c2.getMatchingCondition(request);
assertThat(match1.compareTo(match2, request)).isEqualTo(1);
}
private MockHttpServletRequest createRequest(String requestURI) {
return createRequest("", requestURI);
}
private MockHttpServletRequest createRequest(String contextPath, String requestURI) {
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestURI);
request.setContextPath(contextPath);
ServletRequestPathUtils.parseAndCache(request);
return request;
}
private PathPatternsRequestCondition createCondition(String... patterns) {
return new PathPatternsRequestCondition(parser, patterns);
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.web.servlet.mvc.condition;
import java.util.Arrays;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
@@ -24,28 +23,32 @@ import javax.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.UrlPathHelper;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Unit tests for {@link PatternsRequestCondition}.
*
* @author Rossen Stoyanchev
*/
public class PatternsRequestConditionTests {
class PatternsRequestConditionTests {
@Test
public void prependSlash() {
PatternsRequestCondition c = new PatternsRequestCondition("foo");
assertThat(c.getPatterns().iterator().next()).isEqualTo("/foo");
void prependSlash() {
assertThat(new PatternsRequestCondition("foo").getPatterns().iterator().next())
.isEqualTo("/foo");
}
@Test
public void prependNonEmptyPatternsOnly() {
PatternsRequestCondition c = new PatternsRequestCondition("");
assertThat(c.getPatterns().iterator().next()).as("Do not prepend empty patterns (SPR-8255)").isEqualTo("");
void prependNonEmptyPatternsOnly() {
assertThat(new PatternsRequestCondition("").getPatterns().iterator().next())
.as("Do not prepend empty patterns (SPR-8255)")
.isEqualTo("");
}
@Test
public void combineEmptySets() {
void combineEmptySets() {
PatternsRequestCondition c1 = new PatternsRequestCondition();
PatternsRequestCondition c2 = new PatternsRequestCondition();
PatternsRequestCondition c3 = c1.combine(c2);
@@ -55,7 +58,7 @@ public class PatternsRequestConditionTests {
}
@Test
public void combineOnePatternWithEmptySet() {
void combineOnePatternWithEmptySet() {
PatternsRequestCondition c1 = new PatternsRequestCondition("/type1", "/type2");
PatternsRequestCondition c2 = new PatternsRequestCondition();
@@ -68,7 +71,7 @@ public class PatternsRequestConditionTests {
}
@Test
public void combineMultiplePatterns() {
void combineMultiplePatterns() {
PatternsRequestCondition c1 = new PatternsRequestCondition("/t1", "/t2");
PatternsRequestCondition c2 = new PatternsRequestCondition("/m1", "/m2");
@@ -76,42 +79,49 @@ public class PatternsRequestConditionTests {
}
@Test
public void matchDirectPath() {
void matchDirectPath() {
PatternsRequestCondition condition = new PatternsRequestCondition("/foo");
PatternsRequestCondition match = condition.getMatchingCondition(new MockHttpServletRequest("GET", "/foo"));
PatternsRequestCondition match = condition.getMatchingCondition(initRequest("/foo"));
assertThat(match).isNotNull();
}
@Test
public void matchPattern() {
void matchPattern() {
MockHttpServletRequest request = initRequest("/foo/bar");
PatternsRequestCondition condition = new PatternsRequestCondition("/foo/*");
PatternsRequestCondition match = condition.getMatchingCondition(new MockHttpServletRequest("GET", "/foo/bar"));
PatternsRequestCondition match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
}
@Test
public void matchSortPatterns() {
void matchSortPatterns() {
MockHttpServletRequest request = initRequest("/foo/bar");
PatternsRequestCondition condition = new PatternsRequestCondition("/**", "/foo/bar", "/foo/*");
PatternsRequestCondition match = condition.getMatchingCondition(new MockHttpServletRequest("GET", "/foo/bar"));
PatternsRequestCondition match = condition.getMatchingCondition(request);
PatternsRequestCondition expected = new PatternsRequestCondition("/foo/bar", "/foo/*", "/**");
assertThat(match).isEqualTo(expected);
}
@Test
public void matchSuffixPattern() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo.html");
void matchSuffixPattern() {
MockHttpServletRequest request = initRequest("/foo.html");
PatternsRequestCondition condition = new PatternsRequestCondition("/{foo}");
boolean useSuffixPatternMatch = true;
PatternsRequestCondition condition =
new PatternsRequestCondition(new String[] {"/{foo}"}, null, null, useSuffixPatternMatch, true);
PatternsRequestCondition match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
assertThat(match.getPatterns().iterator().next()).isEqualTo("/{foo}.*");
boolean useSuffixPatternMatch = false;
condition = new PatternsRequestCondition(new String[] {"/{foo}"}, null, null, useSuffixPatternMatch, false);
useSuffixPatternMatch = false;
condition = new PatternsRequestCondition(
new String[] {"/{foo}"}, null, null, useSuffixPatternMatch, false);
match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
@@ -119,17 +129,17 @@ public class PatternsRequestConditionTests {
}
@Test // SPR-8410
public void matchSuffixPatternUsingFileExtensions() {
void matchSuffixPatternUsingFileExtensions() {
PatternsRequestCondition condition = new PatternsRequestCondition(
new String[] {"/jobs/{jobName}"}, null, null, true, false, Collections.singletonList("json"));
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/jobs/my.job");
MockHttpServletRequest request = initRequest("/jobs/my.job");
PatternsRequestCondition match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
assertThat(match.getPatterns().iterator().next()).isEqualTo("/jobs/{jobName}");
request = new MockHttpServletRequest("GET", "/jobs/my.job.json");
request = initRequest("/jobs/my.job.json");
match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
@@ -137,24 +147,24 @@ public class PatternsRequestConditionTests {
}
@Test
public void matchSuffixPatternUsingFileExtensions2() {
void matchSuffixPatternUsingFileExtensions2() {
PatternsRequestCondition condition1 = new PatternsRequestCondition(
new String[] {"/prefix"}, null, null, true, false, Arrays.asList("json"));
new String[] {"/prefix"}, null, null, true, false, Collections.singletonList("json"));
PatternsRequestCondition condition2 = new PatternsRequestCondition(
new String[] {"/suffix"}, null, null, true, false, null);
PatternsRequestCondition combined = condition1.combine(condition2);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/prefix/suffix.json");
MockHttpServletRequest request = initRequest("/prefix/suffix.json");
PatternsRequestCondition match = combined.getMatchingCondition(request);
assertThat(match).isNotNull();
}
@Test
public void matchTrailingSlash() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/");
void matchTrailingSlash() {
MockHttpServletRequest request = initRequest("/foo/");
PatternsRequestCondition condition = new PatternsRequestCondition("/foo");
PatternsRequestCondition match = condition.getMatchingCondition(request);
@@ -162,7 +172,7 @@ public class PatternsRequestConditionTests {
assertThat(match).isNotNull();
assertThat(match.getPatterns().iterator().next()).as("Should match by default").isEqualTo("/foo/");
condition = new PatternsRequestCondition(new String[] {"/foo"}, null, null, false, true);
condition = new PatternsRequestCondition(new String[] {"/foo"}, true, null);
match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
@@ -170,53 +180,53 @@ public class PatternsRequestConditionTests {
.as("Trailing slash should be insensitive to useSuffixPatternMatch settings (SPR-6164, SPR-5636)")
.isEqualTo("/foo/");
condition = new PatternsRequestCondition(new String[] {"/foo"}, null, null, false);
condition = new PatternsRequestCondition(new String[] {"/foo"}, false, null);
match = condition.getMatchingCondition(request);
assertThat(match).isNull();
}
@Test
public void matchPatternContainsExtension() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo.html");
void matchPatternContainsExtension() {
MockHttpServletRequest request = initRequest("/foo.html");
PatternsRequestCondition match = new PatternsRequestCondition("/foo.jpg").getMatchingCondition(request);
assertThat(match).isNull();
}
@Test // gh-22543
public void matchWithEmptyPatterns() {
void matchWithEmptyPatterns() {
PatternsRequestCondition condition = new PatternsRequestCondition();
assertThat(condition.getMatchingCondition(new MockHttpServletRequest("GET", ""))).isNotNull();
assertThat(condition.getMatchingCondition(new MockHttpServletRequest("GET", "/anything"))).isNull();
assertThat(condition.getMatchingCondition(initRequest(""))).isNotNull();
assertThat(condition.getMatchingCondition(initRequest("/anything"))).isNull();
condition = condition.combine(new PatternsRequestCondition());
assertThat(condition.getMatchingCondition(new MockHttpServletRequest("GET", ""))).isNotNull();
assertThat(condition.getMatchingCondition(new MockHttpServletRequest("GET", "/anything"))).isNull();
assertThat(condition.getMatchingCondition(initRequest(""))).isNotNull();
assertThat(condition.getMatchingCondition(initRequest("/anything"))).isNull();
}
@Test
public void compareEqualPatterns() {
void compareEqualPatterns() {
PatternsRequestCondition c1 = new PatternsRequestCondition("/foo*");
PatternsRequestCondition c2 = new PatternsRequestCondition("/foo*");
assertThat(c1.compareTo(c2, new MockHttpServletRequest("GET", "/foo"))).isEqualTo(0);
assertThat(c1.compareTo(c2, initRequest("/foo"))).isEqualTo(0);
}
@Test
public void comparePatternSpecificity() {
void comparePatternSpecificity() {
PatternsRequestCondition c1 = new PatternsRequestCondition("/fo*");
PatternsRequestCondition c2 = new PatternsRequestCondition("/foo");
assertThat(c1.compareTo(c2, new MockHttpServletRequest("GET", "/foo"))).isEqualTo(1);
assertThat(c1.compareTo(c2, initRequest("/foo"))).isEqualTo(1);
}
@Test
public void compareNumberOfMatchingPatterns() {
HttpServletRequest request = new MockHttpServletRequest("GET", "/foo.html");
void compareNumberOfMatchingPatterns() {
HttpServletRequest request = initRequest("/foo.html");
PatternsRequestCondition c1 = new PatternsRequestCondition("/foo", "*.jpeg");
PatternsRequestCondition c2 = new PatternsRequestCondition("/foo", "*.html");
PatternsRequestCondition c1 = new PatternsRequestCondition("/foo.html", "*.jpeg");
PatternsRequestCondition c2 = new PatternsRequestCondition("/foo.html", "*.html");
PatternsRequestCondition match1 = c1.getMatchingCondition(request);
PatternsRequestCondition match2 = c2.getMatchingCondition(request);
@@ -224,4 +234,11 @@ public class PatternsRequestConditionTests {
assertThat(match1.compareTo(match2, request)).isEqualTo(1);
}
private MockHttpServletRequest initRequest(String requestUri) {
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
return request;
}
}

View File

@@ -22,6 +22,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
@@ -31,6 +32,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.RequestPath;
import org.springframework.stereotype.Controller;
import org.springframework.util.MultiValueMap;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
@@ -49,14 +51,11 @@ import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.MappedInterceptor;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -67,9 +66,25 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Arjen Poutsma
* @author Rossen Stoyanchev
*/
public class RequestMappingInfoHandlerMappingTests {
class RequestMappingInfoHandlerMappingTests {
@SuppressWarnings("unused")
static Stream<?> pathPatternsArguments() {
TestController controller = new TestController();
TestRequestMappingInfoHandlerMapping mapping1 = new TestRequestMappingInfoHandlerMapping();
mapping1.setPatternParser(new PathPatternParser());
TestRequestMappingInfoHandlerMapping mapping2 = new TestRequestMappingInfoHandlerMapping();
mapping2.setRemoveSemicolonContent(false);
return Stream.of(mapping1, mapping2).peek(mapping -> {
mapping.setApplicationContext(new StaticWebApplicationContext());
mapping.registerHandler(controller);
mapping.afterPropertiesSet();
});
}
private TestRequestMappingInfoHandlerMapping handlerMapping;
private HandlerMethod fooMethod;
@@ -81,148 +96,145 @@ public class RequestMappingInfoHandlerMappingTests {
@BeforeEach
public void setup() throws Exception {
TestController testController = new TestController();
this.fooMethod = new HandlerMethod(testController, "foo");
this.fooParamMethod = new HandlerMethod(testController, "fooParam");
this.barMethod = new HandlerMethod(testController, "bar");
this.emptyMethod = new HandlerMethod(testController, "empty");
this.handlerMapping = new TestRequestMappingInfoHandlerMapping();
this.handlerMapping.registerHandler(testController);
this.handlerMapping.setRemoveSemicolonContent(false);
void setup() throws Exception {
TestController controller = new TestController();
this.fooMethod = new HandlerMethod(controller, "foo");
this.fooParamMethod = new HandlerMethod(controller, "fooParam");
this.barMethod = new HandlerMethod(controller, "bar");
this.emptyMethod = new HandlerMethod(controller, "empty");
}
@Test
public void getMappingPathPatterns() throws Exception {
@PathPatternsParameterizedTest
void getMappingPathPatterns(TestRequestMappingInfoHandlerMapping mapping) {
String[] patterns = {"/foo/*", "/foo", "/bar/*", "/bar"};
RequestMappingInfo info = RequestMappingInfo.paths(patterns).build();
Set<String> actual = this.handlerMapping.getMappingPathPatterns(info);
RequestMappingInfo info = mapping.createInfo(patterns);
Set<String> actual = mapping.getMappingPathPatterns(info);
assertThat(actual).isEqualTo(new HashSet<>(Arrays.asList(patterns)));
}
@Test
public void getHandlerDirectMatch() throws Exception {
@PathPatternsParameterizedTest
void getHandlerDirectMatch(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
HandlerMethod handlerMethod = getHandler(request);
HandlerMethod handlerMethod = getHandler(mapping, request);
assertThat(handlerMethod.getMethod()).isEqualTo(this.fooMethod.getMethod());
}
@Test
public void getHandlerGlobMatch() throws Exception {
@PathPatternsParameterizedTest
void getHandlerGlobMatch(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bar");
HandlerMethod handlerMethod = getHandler(request);
HandlerMethod handlerMethod = getHandler(mapping, request);
assertThat(handlerMethod.getMethod()).isEqualTo(this.barMethod.getMethod());
}
@Test
public void getHandlerEmptyPathMatch() throws Exception {
@PathPatternsParameterizedTest
void getHandlerEmptyPathMatch(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "");
HandlerMethod handlerMethod = getHandler(request);
HandlerMethod handlerMethod = getHandler(mapping, request);
assertThat(handlerMethod.getMethod()).isEqualTo(this.emptyMethod.getMethod());
request = new MockHttpServletRequest("GET", "/");
handlerMethod = getHandler(request);
handlerMethod = getHandler(mapping, request);
assertThat(handlerMethod.getMethod()).isEqualTo(this.emptyMethod.getMethod());
}
@Test
public void getHandlerBestMatch() throws Exception {
@PathPatternsParameterizedTest
void getHandlerBestMatch(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.setParameter("p", "anything");
HandlerMethod handlerMethod = getHandler(request);
HandlerMethod handlerMethod = getHandler(mapping, request);
assertThat(handlerMethod.getMethod()).isEqualTo(this.fooParamMethod.getMethod());
}
@Test
public void getHandlerRequestMethodNotAllowed() throws Exception {
@PathPatternsParameterizedTest
void getHandlerRequestMethodNotAllowed(TestRequestMappingInfoHandlerMapping mapping) {
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/bar");
assertThatExceptionOfType(HttpRequestMethodNotSupportedException.class).isThrownBy(() ->
this.handlerMapping.getHandler(request))
.satisfies(ex -> assertThat(ex.getSupportedMethods()).containsExactly("GET", "HEAD"));
assertThatExceptionOfType(HttpRequestMethodNotSupportedException.class)
.isThrownBy(() -> mapping.getHandler(request))
.satisfies(ex -> assertThat(ex.getSupportedMethods()).containsExactly("GET", "HEAD"));
}
@Test // SPR-9603
public void getHandlerRequestMethodMatchFalsePositive() throws Exception {
@PathPatternsParameterizedTest // SPR-9603
void getHandlerRequestMethodMatchFalsePositive(TestRequestMappingInfoHandlerMapping mapping) {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/users");
request.addHeader("Accept", "application/xml");
this.handlerMapping.registerHandler(new UserController());
assertThatExceptionOfType(HttpMediaTypeNotAcceptableException.class).isThrownBy(() ->
this.handlerMapping.getHandler(request));
mapping.registerHandler(new UserController());
assertThatExceptionOfType(HttpMediaTypeNotAcceptableException.class)
.isThrownBy(() -> mapping.getHandler(request));
}
@Test // SPR-8462
public void getHandlerMediaTypeNotSupported() throws Exception {
testHttpMediaTypeNotSupportedException("/person/1");
testHttpMediaTypeNotSupportedException("/person/1/");
testHttpMediaTypeNotSupportedException("/person/1.json");
@PathPatternsParameterizedTest // SPR-8462
void getHandlerMediaTypeNotSupported(TestRequestMappingInfoHandlerMapping mapping) {
testHttpMediaTypeNotSupportedException(mapping, "/person/1");
testHttpMediaTypeNotSupportedException(mapping, "/person/1/");
testHttpMediaTypeNotSupportedException(mapping, "/person/1.json");
}
@Test
public void getHandlerHttpOptions() throws Exception {
testHttpOptions("/foo", "GET,HEAD,OPTIONS");
testHttpOptions("/person/1", "PUT,OPTIONS");
testHttpOptions("/persons", "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS");
testHttpOptions("/something", "PUT,POST");
@PathPatternsParameterizedTest
void getHandlerHttpOptions(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
testHttpOptions(mapping, "/foo", "GET,HEAD,OPTIONS");
testHttpOptions(mapping, "/person/1", "PUT,OPTIONS");
testHttpOptions(mapping, "/persons", "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS");
testHttpOptions(mapping, "/something", "PUT,POST");
}
@Test
public void getHandlerTestInvalidContentType() throws Exception {
@PathPatternsParameterizedTest
void getHandlerTestInvalidContentType(TestRequestMappingInfoHandlerMapping mapping) {
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/person/1");
request.setContentType("bogus");
assertThatExceptionOfType(HttpMediaTypeNotSupportedException.class).isThrownBy(() ->
this.handlerMapping.getHandler(request))
.withMessage("Invalid mime type \"bogus\": does not contain '/'");
assertThatExceptionOfType(HttpMediaTypeNotSupportedException.class)
.isThrownBy(() -> mapping.getHandler(request))
.withMessage("Invalid mime type \"bogus\": does not contain '/'");
}
@Test // SPR-8462
public void getHandlerMediaTypeNotAccepted() throws Exception {
testHttpMediaTypeNotAcceptableException("/persons");
testHttpMediaTypeNotAcceptableException("/persons/");
testHttpMediaTypeNotAcceptableException("/persons.json");
@PathPatternsParameterizedTest // SPR-8462
void getHandlerMediaTypeNotAccepted(TestRequestMappingInfoHandlerMapping mapping) {
testHttpMediaTypeNotAcceptableException(mapping, "/persons");
testHttpMediaTypeNotAcceptableException(mapping, "/persons/");
if (mapping.getPatternParser() == null) {
testHttpMediaTypeNotAcceptableException(mapping, "/persons.json");
}
}
@Test // SPR-12854
public void getHandlerUnsatisfiedServletRequestParameterException() throws Exception {
@PathPatternsParameterizedTest // SPR-12854
void getHandlerUnsatisfiedServletRequestParameterException(TestRequestMappingInfoHandlerMapping mapping) {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/params");
assertThatExceptionOfType(UnsatisfiedServletRequestParameterException.class).isThrownBy(() ->
this.handlerMapping.getHandler(request))
.satisfies(ex -> assertThat(ex.getParamConditionGroups().stream().map(group -> group[0]))
.containsExactlyInAnyOrder("foo=bar", "bar=baz"));
assertThatExceptionOfType(UnsatisfiedServletRequestParameterException.class)
.isThrownBy(() -> mapping.getHandler(request))
.satisfies(ex -> assertThat(ex.getParamConditionGroups().stream().map(group -> group[0]))
.containsExactlyInAnyOrder("foo=bar", "bar=baz"));
}
@Test
public void getHandlerProducibleMediaTypesAttribute() throws Exception {
@PathPatternsParameterizedTest
void getHandlerProducibleMediaTypesAttribute(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/content");
request.addHeader("Accept", "application/xml");
this.handlerMapping.getHandler(request);
mapping.getHandler(request);
String name = HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
assertThat(request.getAttribute(name)).isEqualTo(Collections.singleton(MediaType.APPLICATION_XML));
request = new MockHttpServletRequest("GET", "/content");
request.addHeader("Accept", "application/json");
this.handlerMapping.getHandler(request);
mapping.getHandler(request);
assertThat(request.getAttribute(name)).as("Negated expression shouldn't be listed as producible type").isNull();
}
@Test
public void getHandlerMappedInterceptors() throws Exception {
void getHandlerMappedInterceptors() throws Exception {
String path = "/foo";
HandlerInterceptor interceptor = new HandlerInterceptor() {};
MappedInterceptor mappedInterceptor = new MappedInterceptor(new String[] {path}, interceptor);
TestRequestMappingInfoHandlerMapping mapping = new TestRequestMappingInfoHandlerMapping();
mapping.registerHandler(new TestController());
mapping.setInterceptors(new Object[] { mappedInterceptor });
mapping.setInterceptors(mappedInterceptor);
mapping.setApplicationContext(new StaticWebApplicationContext());
HandlerExecutionChain chain = mapping.getHandler(new MockHttpServletRequest("GET", path));
@@ -235,12 +247,12 @@ public class RequestMappingInfoHandlerMappingTests {
}
@SuppressWarnings("unchecked")
@Test
public void handleMatchUriTemplateVariables() {
@PathPatternsParameterizedTest
void handleMatchUriTemplateVariables(TestRequestMappingInfoHandlerMapping mapping) {
RequestMappingInfo key = RequestMappingInfo.paths("/{path1}/{path2}").build();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/1/2");
String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
this.handlerMapping.handleMatch(key, lookupPath, request);
mapping.handleMatch(key, lookupPath, request);
String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
Map<String, String> uriVariables = (Map<String, String>) request.getAttribute(name);
@@ -251,8 +263,8 @@ public class RequestMappingInfoHandlerMappingTests {
}
@SuppressWarnings("unchecked")
@Test // SPR-9098
public void handleMatchUriTemplateVariablesDecode() {
@PathPatternsParameterizedTest // SPR-9098
void handleMatchUriTemplateVariablesDecode(TestRequestMappingInfoHandlerMapping mapping) {
RequestMappingInfo key = RequestMappingInfo.paths("/{group}/{identifier}").build();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/group/a%2Fb");
@@ -260,8 +272,8 @@ public class RequestMappingInfoHandlerMappingTests {
pathHelper.setUrlDecode(false);
String lookupPath = pathHelper.getLookupPathForRequest(request);
this.handlerMapping.setUrlPathHelper(pathHelper);
this.handlerMapping.handleMatch(key, lookupPath, request);
mapping.setUrlPathHelper(pathHelper);
mapping.handleMatch(key, lookupPath, request);
String name = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
Map<String, String> uriVariables = (Map<String, String>) request.getAttribute(name);
@@ -271,32 +283,32 @@ public class RequestMappingInfoHandlerMappingTests {
assertThat(uriVariables.get("identifier")).isEqualTo("a/b");
}
@Test
public void handleMatchBestMatchingPatternAttribute() {
@PathPatternsParameterizedTest
void handleMatchBestMatchingPatternAttribute(TestRequestMappingInfoHandlerMapping mapping) {
RequestMappingInfo key = RequestMappingInfo.paths("/{path1}/2", "/**").build();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/1/2");
this.handlerMapping.handleMatch(key, "/1/2", request);
mapping.handleMatch(key, "/1/2", request);
assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)).isEqualTo("/{path1}/2");
}
@Test // gh-22543
public void handleMatchBestMatchingPatternAttributeNoPatternsDefined() {
@PathPatternsParameterizedTest // gh-22543
void handleMatchBestMatchingPatternAttributeNoPatternsDefined(TestRequestMappingInfoHandlerMapping mapping) {
String path = "";
MockHttpServletRequest request = new MockHttpServletRequest("GET", path);
this.handlerMapping.handleMatch(RequestMappingInfo.paths().build(), path, request);
mapping.handleMatch(RequestMappingInfo.paths().build(), path, request);
assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)).isEqualTo(path);
}
@Test
public void handleMatchMatrixVariables() {
@PathPatternsParameterizedTest
void handleMatchMatrixVariables(TestRequestMappingInfoHandlerMapping mapping) {
MockHttpServletRequest request;
MultiValueMap<String, String> matrixVariables;
Map<String, String> uriVariables;
// URI var parsed into path variable + matrix params..
request = new MockHttpServletRequest();
handleMatch(request, "/{cars}", "/cars;colors=red,blue,green;year=2012");
request = new MockHttpServletRequest("GET", "/cars;colors=red,blue,green;year=2012");
handleMatch(mapping, request, "/{cars}", request.getRequestURI());
matrixVariables = getMatrixVariables(request, "cars");
uriVariables = getUriTemplateVariables(request);
@@ -307,8 +319,8 @@ public class RequestMappingInfoHandlerMappingTests {
assertThat(uriVariables.get("cars")).isEqualTo("cars");
// URI var with regex for path variable, and URI var for matrix params..
request = new MockHttpServletRequest();
handleMatch(request, "/{cars:[^;]+}{params}", "/cars;colors=red,blue,green;year=2012");
request = new MockHttpServletRequest("GET", "/cars;colors=red,blue,green;year=2012");
handleMatch(mapping, request, "/{cars:[^;]+}{params}", request.getRequestURI());
matrixVariables = getMatrixVariables(request, "params");
uriVariables = getUriTemplateVariables(request);
@@ -317,11 +329,13 @@ public class RequestMappingInfoHandlerMappingTests {
assertThat(matrixVariables.get("colors")).isEqualTo(Arrays.asList("red", "blue", "green"));
assertThat(matrixVariables.getFirst("year")).isEqualTo("2012");
assertThat(uriVariables.get("cars")).isEqualTo("cars");
assertThat(uriVariables.get("params")).isEqualTo(";colors=red,blue,green;year=2012");
if (mapping.getPatternParser() == null) {
assertThat(uriVariables.get("params")).isEqualTo(";colors=red,blue,green;year=2012");
}
// URI var with regex for path variable, and (empty) URI var for matrix params..
request = new MockHttpServletRequest();
handleMatch(request, "/{cars:[^;]+}{params}", "/cars");
request = new MockHttpServletRequest("GET", "/cars");
handleMatch(mapping, request, "/{cars:[^;]+}{params}", request.getRequestURI());
matrixVariables = getMatrixVariables(request, "params");
uriVariables = getUriTemplateVariables(request);
@@ -331,32 +345,38 @@ public class RequestMappingInfoHandlerMappingTests {
assertThat(uriVariables.get("params")).isEqualTo("");
// SPR-11897
request = new MockHttpServletRequest();
handleMatch(request, "/{foo}", "/a=42;b=c");
request = new MockHttpServletRequest("GET", "/a=42;b=c");
handleMatch(mapping, request, "/{foo}", request.getRequestURI());
matrixVariables = getMatrixVariables(request, "foo");
uriVariables = getUriTemplateVariables(request);
assertThat(matrixVariables).isNotNull();
assertThat(matrixVariables.size()).isEqualTo(2);
assertThat(matrixVariables.getFirst("a")).isEqualTo("42");
assertThat(matrixVariables.getFirst("b")).isEqualTo("c");
assertThat(uriVariables.get("foo")).isEqualTo("a=42");
if (mapping.getPatternParser() != null) {
assertThat(matrixVariables.size()).isEqualTo(1);
assertThat(matrixVariables.getFirst("b")).isEqualTo("c");
assertThat(uriVariables.get("foo")).isEqualTo("a=42");
}
else {
assertThat(matrixVariables.size()).isEqualTo(2);
assertThat(matrixVariables.getFirst("a")).isEqualTo("42");
assertThat(matrixVariables.getFirst("b")).isEqualTo("c");
assertThat(uriVariables.get("foo")).isEqualTo("a=42");
}
}
@Test // SPR-10140, SPR-16867
public void handleMatchMatrixVariablesDecoding() {
@PathPatternsParameterizedTest // SPR-10140, SPR-16867
void handleMatchMatrixVariablesDecoding(TestRequestMappingInfoHandlerMapping mapping) {
MockHttpServletRequest request;
if (mapping.getPatternParser() == null) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setUrlDecode(false);
urlPathHelper.setRemoveSemicolonContent(false);
mapping.setUrlPathHelper(urlPathHelper);
}
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setUrlDecode(false);
urlPathHelper.setRemoveSemicolonContent(false);
this.handlerMapping.setUrlPathHelper(urlPathHelper);
request = new MockHttpServletRequest();
handleMatch(request, "/{cars}", "/cars;mvar=a%2Fb");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/cars;mvar=a%2Fb");
handleMatch(mapping, request, "/{cars}", request.getRequestURI());
MultiValueMap<String, String> matrixVariables = getMatrixVariables(request, "cars");
Map<String, String> uriVariables = getUriTemplateVariables(request);
@@ -366,24 +386,27 @@ public class RequestMappingInfoHandlerMappingTests {
assertThat(uriVariables.get("cars")).isEqualTo("cars");
}
private HandlerMethod getHandler(
TestRequestMappingInfoHandlerMapping mapping, MockHttpServletRequest request) throws Exception {
private HandlerMethod getHandler(MockHttpServletRequest request) throws Exception {
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
assertThat(chain).isNotNull();
return (HandlerMethod) chain.getHandler();
}
private void testHttpMediaTypeNotSupportedException(String url) throws Exception {
private void testHttpMediaTypeNotSupportedException(TestRequestMappingInfoHandlerMapping mapping, String url) {
MockHttpServletRequest request = new MockHttpServletRequest("PUT", url);
request.setContentType("application/json");
assertThatExceptionOfType(HttpMediaTypeNotSupportedException.class).isThrownBy(() ->
this.handlerMapping.getHandler(request))
.satisfies(ex -> assertThat(ex.getSupportedMediaTypes()).containsExactly(MediaType.APPLICATION_XML));
assertThatExceptionOfType(HttpMediaTypeNotSupportedException.class)
.isThrownBy(() -> mapping.getHandler(request))
.satisfies(ex -> assertThat(ex.getSupportedMediaTypes()).containsExactly(MediaType.APPLICATION_XML));
}
private void testHttpOptions(String requestURI, String allowHeader) throws Exception {
private void testHttpOptions(
TestRequestMappingInfoHandlerMapping mapping, String requestURI, String allowHeader) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("OPTIONS", requestURI);
HandlerMethod handlerMethod = getHandler(request);
HandlerMethod handlerMethod = getHandler(mapping, request);
ServletWebRequest webRequest = new ServletWebRequest(request);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
@@ -394,17 +417,22 @@ public class RequestMappingInfoHandlerMappingTests {
assertThat(((HttpHeaders) result).getFirst("Allow")).isEqualTo(allowHeader);
}
private void testHttpMediaTypeNotAcceptableException(String url) throws Exception {
private void testHttpMediaTypeNotAcceptableException(TestRequestMappingInfoHandlerMapping mapping, String url) {
MockHttpServletRequest request = new MockHttpServletRequest("GET", url);
request.addHeader("Accept", "application/json");
assertThatExceptionOfType(HttpMediaTypeNotAcceptableException.class).isThrownBy(() ->
this.handlerMapping.getHandler(request))
mapping.getHandler(request))
.satisfies(ex -> assertThat(ex.getSupportedMediaTypes()).containsExactly(MediaType.APPLICATION_XML));
}
private void handleMatch(MockHttpServletRequest request, String pattern, String lookupPath) {
RequestMappingInfo info = RequestMappingInfo.paths(pattern).build();
this.handlerMapping.handleMatch(info, lookupPath, request);
private void handleMatch(TestRequestMappingInfoHandlerMapping mapping,
MockHttpServletRequest request, String pattern, String lookupPath) {
if (mapping.getPatternParser() != null) {
ServletRequestPathUtils.parseAndCache(request);
}
mapping.handleMatch(mapping.createInfo(pattern), lookupPath, request);
}
@SuppressWarnings("unchecked")
@@ -494,7 +522,8 @@ public class RequestMappingInfoHandlerMappingTests {
private static class TestRequestMappingInfoHandlerMapping extends RequestMappingInfoHandlerMapping {
public void registerHandler(Object handler) {
void registerHandler(Object handler) {
super.detectHandlerMethods(handler);
}
@@ -507,18 +536,46 @@ public class RequestMappingInfoHandlerMappingTests {
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMapping annot = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (annot != null) {
return new RequestMappingInfo(
new PatternsRequestCondition(annot.value(), getUrlPathHelper(), getPathMatcher(), true, true),
new RequestMethodsRequestCondition(annot.method()),
new ParamsRequestCondition(annot.params()),
new HeadersRequestCondition(annot.headers()),
new ConsumesRequestCondition(annot.consumes(), annot.headers()),
new ProducesRequestCondition(annot.produces(), annot.headers()), null);
return RequestMappingInfo.paths(annot.value())
.methods(annot.method())
.params(annot.params())
.headers(annot.headers())
.consumes(annot.consumes())
.produces(annot.produces())
.options(getBuilderConfig())
.build();
}
else {
return null;
}
}
private RequestMappingInfo.BuilderConfiguration getBuilderConfig() {
RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
if (getPatternParser() != null) {
config.setPatternParser(getPatternParser());
}
else {
config.setSuffixPatternMatch(true);
config.setRegisteredSuffixPatternMatch(true);
config.setPathMatcher(getPathMatcher());
}
return config;
}
RequestMappingInfo createInfo(String... patterns) {
return RequestMappingInfo.paths(patterns).options(getBuilderConfig()).build();
}
@Override
protected String initLookupPath(HttpServletRequest request) {
// At runtime this is done by the DispatcherServlet
if (getPatternParser() != null) {
RequestPath requestPath = ServletRequestPathUtils.parseAndCache(request);
return requestPath.pathWithinApplication().value();
}
return super.initLookupPath(request);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -19,6 +19,7 @@ package org.springframework.web.servlet.mvc.method;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
@@ -26,13 +27,16 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.PathPatternParser;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.HEAD;
import static org.springframework.web.servlet.mvc.method.RequestMappingInfo.paths;
/**
* Test fixture for {@link RequestMappingInfo} tests.
@@ -40,15 +44,22 @@ import static org.springframework.web.servlet.mvc.method.RequestMappingInfo.path
* @author Arjen Poutsma
* @author Rossen Stoyanchev
*/
public class RequestMappingInfoTests {
class RequestMappingInfoTests {
@Test
public void createEmpty() {
@SuppressWarnings("unused")
static Stream<RequestMappingInfo.Builder> pathPatternsArguments() {
RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
config.setPatternParser(new PathPatternParser());
return Stream.of(RequestMappingInfo.paths().options(config), RequestMappingInfo.paths());
}
RequestMappingInfo info = paths().build();
@PathPatternsParameterizedTest
void createEmpty(RequestMappingInfo.Builder infoBuilder) {
// gh-22543
assertThat(info.getPatternsCondition().getPatterns()).isEqualTo(Collections.singleton(""));
RequestMappingInfo info = infoBuilder.build();
assertThat(info.getPatternValues()).isEqualTo(Collections.singleton(""));
assertThat(info.getMethodsCondition().getMethods().size()).isEqualTo(0);
assertThat(info.getParamsCondition()).isNotNull();
assertThat(info.getHeadersCondition()).isNotNull();
@@ -56,7 +67,8 @@ public class RequestMappingInfoTests {
assertThat(info.getProducesCondition().isEmpty()).isEqualTo(true);
assertThat(info.getCustomCondition()).isNull();
RequestMappingInfo anotherInfo = paths().build();
RequestMappingInfo anotherInfo = infoBuilder.build();
assertThat(info.getActivePatternsCondition()).isSameAs(anotherInfo.getActivePatternsCondition());
assertThat(info.getPatternsCondition()).isSameAs(anotherInfo.getPatternsCondition());
assertThat(info.getMethodsCondition()).isSameAs(anotherInfo.getMethodsCondition());
assertThat(info.getParamsCondition()).isSameAs(anotherInfo.getParamsCondition());
@@ -66,7 +78,7 @@ public class RequestMappingInfoTests {
assertThat(info.getCustomCondition()).isSameAs(anotherInfo.getCustomCondition());
RequestMappingInfo result = info.combine(anotherInfo);
assertThat(info.getPatternsCondition()).isSameAs(result.getPatternsCondition());
assertThat(info.getActivePatternsCondition()).isSameAs(result.getActivePatternsCondition());
assertThat(info.getMethodsCondition()).isSameAs(result.getMethodsCondition());
assertThat(info.getParamsCondition()).isSameAs(result.getParamsCondition());
assertThat(info.getHeadersCondition()).isSameAs(result.getHeadersCondition());
@@ -75,148 +87,157 @@ public class RequestMappingInfoTests {
assertThat(info.getCustomCondition()).isSameAs(result.getCustomCondition());
}
@Test
public void matchPatternsCondition() {
HttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
@PathPatternsParameterizedTest
void matchPatternsCondition(RequestMappingInfo.Builder builder) {
RequestMappingInfo info = paths("/foo*", "/bar").build();
RequestMappingInfo expected = paths("/foo*").build();
boolean useParsedPatterns = builder.build().getPathPatternsCondition() != null;
HttpServletRequest request = PathPatternsTestUtils.initRequest("GET", "/foo", useParsedPatterns);
RequestMappingInfo info = builder.paths("/foo*", "/bar").build();
RequestMappingInfo expected = builder.paths("/foo*").build();
assertThat(info.getMatchingCondition(request)).isEqualTo(expected);
info = paths("/**", "/foo*", "/foo").build();
expected = paths("/foo", "/foo*", "/**").build();
info = builder.paths("/**", "/foo*", "/foo").build();
expected = builder.paths("/foo", "/foo*", "/**").build();
assertThat(info.getMatchingCondition(request)).isEqualTo(expected);
}
@Test
public void matchParamsCondition() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
void matchParamsCondition() {
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("GET", "/foo", false);
request.setParameter("foo", "bar");
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
RequestMappingInfo info = paths("/foo").params("foo=bar").build();
RequestMappingInfo info = RequestMappingInfo.paths("/foo").params("foo=bar").build();
RequestMappingInfo match = info.getMatchingCondition(request);
assertThat(match).isNotNull();
info = paths("/foo").params("foo!=bar").build();
info = RequestMappingInfo.paths("/foo").params("foo!=bar").build();
match = info.getMatchingCondition(request);
assertThat(match).isNull();
}
@Test
public void matchHeadersCondition() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
void matchHeadersCondition() {
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("GET", "/foo", false);
request.addHeader("foo", "bar");
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
RequestMappingInfo info = paths("/foo").headers("foo=bar").build();
RequestMappingInfo info = RequestMappingInfo.paths("/foo").headers("foo=bar").build();
RequestMappingInfo match = info.getMatchingCondition(request);
assertThat(match).isNotNull();
info = paths("/foo").headers("foo!=bar").build();
info = RequestMappingInfo.paths("/foo").headers("foo!=bar").build();
match = info.getMatchingCondition(request);
assertThat(match).isNull();
}
@Test
public void matchConsumesCondition() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
void matchConsumesCondition() {
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("GET", "/foo", false);
request.setContentType("text/plain");
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
RequestMappingInfo info = paths("/foo").consumes("text/plain").build();
RequestMappingInfo info = RequestMappingInfo.paths("/foo").consumes("text/plain").build();
RequestMappingInfo match = info.getMatchingCondition(request);
assertThat(match).isNotNull();
info = paths("/foo").consumes("application/xml").build();
info = RequestMappingInfo.paths("/foo").consumes("application/xml").build();
match = info.getMatchingCondition(request);
assertThat(match).isNull();
}
@Test
public void matchProducesCondition() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
void matchProducesCondition() {
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("GET", "/foo", false);
request.addHeader("Accept", "text/plain");
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
RequestMappingInfo info = paths("/foo").produces("text/plain").build();
RequestMappingInfo info = RequestMappingInfo.paths("/foo").produces("text/plain").build();
RequestMappingInfo match = info.getMatchingCondition(request);
assertThat(match).isNotNull();
info = paths("/foo").produces("application/xml").build();
info = RequestMappingInfo.paths("/foo").produces("application/xml").build();
match = info.getMatchingCondition(request);
assertThat(match).isNull();
}
@Test
public void matchCustomCondition() {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
void matchCustomCondition() {
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("GET", "/foo", false);
request.setParameter("foo", "bar");
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
RequestMappingInfo info = paths("/foo").params("foo=bar").build();
RequestMappingInfo info = RequestMappingInfo.paths("/foo").params("foo=bar").build();
RequestMappingInfo match = info.getMatchingCondition(request);
assertThat(match).isNotNull();
info = paths("/foo").params("foo!=bar").params("foo!=bar").build();
info = RequestMappingInfo.paths("/foo").params("foo!=bar").params("foo!=bar").build();
match = info.getMatchingCondition(request);
assertThat(match).isNull();
}
@Test
public void compareToWithImpicitVsExplicitHttpMethodDeclaration() {
RequestMappingInfo noMethods = paths().build();
RequestMappingInfo oneMethod = paths().methods(GET).build();
RequestMappingInfo oneMethodOneParam = paths().methods(GET).params("foo").build();
void compareToWithImpicitVsExplicitHttpMethodDeclaration() {
RequestMappingInfo noMethods = RequestMappingInfo.paths().build();
RequestMappingInfo oneMethod = RequestMappingInfo.paths().methods(GET).build();
RequestMappingInfo oneMethodOneParam = RequestMappingInfo.paths().methods(GET).params("foo").build();
Comparator<RequestMappingInfo> comparator =
(info, otherInfo) -> info.compareTo(otherInfo, new MockHttpServletRequest());
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("GET", "/", false);
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
Comparator<RequestMappingInfo> comparator = (info, otherInfo) -> info.compareTo(otherInfo, request);
List<RequestMappingInfo> list = asList(noMethods, oneMethod, oneMethodOneParam);
Collections.shuffle(list);
Collections.sort(list, comparator);
list.sort(comparator);
assertThat(list.get(0)).isEqualTo(oneMethodOneParam);
assertThat(list.get(1)).isEqualTo(oneMethod);
assertThat(list.get(2)).isEqualTo(noMethods);
}
@Test // SPR-14383
public void compareToWithHttpHeadMapping() {
MockHttpServletRequest request = new MockHttpServletRequest();
@Test
// SPR-14383
void compareToWithHttpHeadMapping() {
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("GET", "/", false);
request.setMethod("HEAD");
request.addHeader("Accept", "application/json");
RequestMappingInfo noMethods = paths().build();
RequestMappingInfo getMethod = paths().methods(GET).produces("application/json").build();
RequestMappingInfo headMethod = paths().methods(HEAD).build();
RequestMappingInfo noMethods = RequestMappingInfo.paths().build();
RequestMappingInfo getMethod = RequestMappingInfo.paths().methods(GET).produces("application/json").build();
RequestMappingInfo headMethod = RequestMappingInfo.paths().methods(HEAD).build();
Comparator<RequestMappingInfo> comparator = (info, otherInfo) -> info.compareTo(otherInfo, request);
List<RequestMappingInfo> list = asList(noMethods, getMethod, headMethod);
Collections.shuffle(list);
Collections.sort(list, comparator);
list.sort(comparator);
assertThat(list.get(0)).isEqualTo(headMethod);
assertThat(list.get(1)).isEqualTo(getMethod);
assertThat(list.get(2)).isEqualTo(noMethods);
}
@Test
public void equals() {
RequestMappingInfo info1 = paths("/foo").methods(GET)
@PathPatternsParameterizedTest
void equalsMethod(RequestMappingInfo.Builder infoBuilder) {
RequestMappingInfo info1 = infoBuilder.paths("/foo").methods(GET)
.params("foo=bar", "customFoo=customBar").headers("foo=bar")
.consumes("text/plain").produces("text/plain")
.build();
RequestMappingInfo info2 = paths("/foo").methods(GET)
RequestMappingInfo info2 = infoBuilder.paths("/foo").methods(GET)
.params("foo=bar", "customFoo=customBar").headers("foo=bar")
.consumes("text/plain").produces("text/plain")
.build();
@@ -224,7 +245,7 @@ public class RequestMappingInfoTests {
assertThat(info2).isEqualTo(info1);
assertThat(info2.hashCode()).isEqualTo(info1.hashCode());
info2 = paths("/foo", "/NOOOOOO").methods(GET)
info2 = infoBuilder.paths("/foo", "/NOOOOOO").methods(GET)
.params("foo=bar", "customFoo=customBar").headers("foo=bar")
.consumes("text/plain").produces("text/plain")
.build();
@@ -232,7 +253,7 @@ public class RequestMappingInfoTests {
assertThat(info1.equals(info2)).isFalse();
assertThat(info2.hashCode()).isNotEqualTo((long) info1.hashCode());
info2 = paths("/foo").methods(GET, RequestMethod.POST)
info2 = infoBuilder.paths("/foo").methods(GET, RequestMethod.POST)
.params("foo=bar", "customFoo=customBar").headers("foo=bar")
.consumes("text/plain").produces("text/plain")
.build();
@@ -240,7 +261,7 @@ public class RequestMappingInfoTests {
assertThat(info1.equals(info2)).isFalse();
assertThat(info2.hashCode()).isNotEqualTo((long) info1.hashCode());
info2 = paths("/foo").methods(GET)
info2 = infoBuilder.paths("/foo").methods(GET)
.params("/NOOOOOO", "customFoo=customBar").headers("foo=bar")
.consumes("text/plain").produces("text/plain")
.build();
@@ -248,7 +269,7 @@ public class RequestMappingInfoTests {
assertThat(info1.equals(info2)).isFalse();
assertThat(info2.hashCode()).isNotEqualTo((long) info1.hashCode());
info2 = paths("/foo").methods(GET)
info2 = infoBuilder.paths("/foo").methods(GET)
.params("foo=bar", "customFoo=customBar").headers("/NOOOOOO")
.consumes("text/plain").produces("text/plain")
.build();
@@ -256,7 +277,7 @@ public class RequestMappingInfoTests {
assertThat(info1.equals(info2)).isFalse();
assertThat(info2.hashCode()).isNotEqualTo((long) info1.hashCode());
info2 = paths("/foo").methods(GET)
info2 = infoBuilder.paths("/foo").methods(GET)
.params("foo=bar", "customFoo=customBar").headers("foo=bar")
.consumes("text/NOOOOOO").produces("text/plain")
.build();
@@ -264,7 +285,7 @@ public class RequestMappingInfoTests {
assertThat(info1.equals(info2)).isFalse();
assertThat(info2.hashCode()).isNotEqualTo((long) info1.hashCode());
info2 = paths("/foo").methods(GET)
info2 = infoBuilder.paths("/foo").methods(GET)
.params("foo=bar", "customFoo=customBar").headers("foo=bar")
.consumes("text/plain").produces("text/NOOOOOO")
.build();
@@ -272,7 +293,7 @@ public class RequestMappingInfoTests {
assertThat(info1.equals(info2)).isFalse();
assertThat(info2.hashCode()).isNotEqualTo((long) info1.hashCode());
info2 = paths("/foo").methods(GET)
info2 = infoBuilder.paths("/foo").methods(GET)
.params("foo=bar", "customFoo=NOOOOOO").headers("foo=bar")
.consumes("text/plain").produces("text/plain")
.build();
@@ -282,16 +303,16 @@ public class RequestMappingInfoTests {
}
@Test
public void preFlightRequest() {
MockHttpServletRequest request = new MockHttpServletRequest("OPTIONS", "/foo");
void preFlightRequest() {
MockHttpServletRequest request = PathPatternsTestUtils.initRequest("OPTIONS", "/foo", false);
request.addHeader(HttpHeaders.ORIGIN, "https://domain.com");
request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST");
RequestMappingInfo info = paths("/foo").methods(RequestMethod.POST).build();
RequestMappingInfo info = RequestMappingInfo.paths("/foo").methods(RequestMethod.POST).build();
RequestMappingInfo match = info.getMatchingCondition(request);
assertThat(match).isNotNull();
info = paths("/foo").methods(RequestMethod.OPTIONS).build();
info = RequestMappingInfo.paths("/foo").methods(RequestMethod.OPTIONS).build();
match = info.getMatchingCondition(request);
assertThat(match).as("Pre-flight should match the ACCESS_CONTROL_REQUEST_METHOD").isNull();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -20,6 +20,7 @@ import javax.servlet.ServletException;
import org.junit.jupiter.api.AfterEach;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.lang.Nullable;
@@ -29,6 +30,7 @@ import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
import org.springframework.web.testfixture.servlet.MockServletConfig;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
@@ -44,6 +46,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public abstract class AbstractServletHandlerMethodTests {
@Nullable
private DispatcherServlet servlet;
@@ -57,49 +60,54 @@ public abstract class AbstractServletHandlerMethodTests {
this.servlet = null;
}
/**
* Initialize a DispatcherServlet instance registering zero or more controller classes.
*/
protected WebApplicationContext initServletWithControllers(final Class<?>... controllerClasses)
throws ServletException {
protected WebApplicationContext initDispatcherServlet(
Class<?> controllerClass, boolean usePathPatterns) throws ServletException {
return initServlet(null, controllerClasses);
return initDispatcherServlet(controllerClass, usePathPatterns, null);
}
/**
* Initialize a DispatcherServlet instance registering zero or more controller classes
* and also providing additional bean definitions through a callback.
*/
@SuppressWarnings("serial")
protected WebApplicationContext initServlet(
final ApplicationContextInitializer<GenericWebApplicationContext> initializer,
final Class<?>... controllerClasses) throws ServletException {
WebApplicationContext initDispatcherServlet(
@Nullable Class<?> controllerClass, boolean usePathPatterns,
@Nullable ApplicationContextInitializer<GenericWebApplicationContext> initializer)
throws ServletException {
final GenericWebApplicationContext wac = new GenericWebApplicationContext();
servlet = new DispatcherServlet() {
@Override
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
for (Class<?> clazz : controllerClasses) {
wac.registerBeanDefinition(clazz.getSimpleName(), new RootBeanDefinition(clazz));
}
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("removeSemicolonContent", "false");
wac.registerBeanDefinition("handlerMapping", mappingDef);
wac.registerBeanDefinition("handlerAdapter",
new RootBeanDefinition(RequestMappingHandlerAdapter.class));
wac.registerBeanDefinition("requestMappingResolver",
new RootBeanDefinition(ExceptionHandlerExceptionResolver.class));
wac.registerBeanDefinition("responseStatusResolver",
new RootBeanDefinition(ResponseStatusExceptionResolver.class));
wac.registerBeanDefinition("defaultResolver",
new RootBeanDefinition(DefaultHandlerExceptionResolver.class));
if (controllerClass != null) {
wac.registerBeanDefinition(
controllerClass.getSimpleName(), new RootBeanDefinition(controllerClass));
}
if (initializer != null) {
initializer.initialize(wac);
}
if (!wac.containsBeanDefinition("handlerMapping")) {
BeanDefinition def = register("handlerMapping", RequestMappingHandlerMapping.class, wac);
def.getPropertyValues().add("removeSemicolonContent", "false");
}
BeanDefinition mappingDef = wac.getBeanDefinition("handlerMapping");
if (usePathPatterns && !mappingDef.hasAttribute("patternParser")) {
BeanDefinition parserDef = register("parser", PathPatternParser.class, wac);
mappingDef.getPropertyValues().add("patternParser", parserDef);
}
register("handlerAdapter", RequestMappingHandlerAdapter.class, wac);
register("requestMappingResolver", ExceptionHandlerExceptionResolver.class, wac);
register("responseStatusResolver", ResponseStatusExceptionResolver.class, wac);
register("defaultResolver", DefaultHandlerExceptionResolver.class, wac);
wac.refresh();
return wac;
}
@@ -110,4 +118,13 @@ public abstract class AbstractServletHandlerMethodTests {
return wac;
}
private BeanDefinition register(String beanName, Class<?> beanType, GenericWebApplicationContext wac) {
if (wac.containsBeanDefinition(beanName)) {
return wac.getBeanDefinition(beanName);
}
RootBeanDefinition beanDef = new RootBeanDefinition(beanType);
wac.registerBeanDefinition(beanName, beanDef);
return beanDef;
}
}

View File

@@ -21,11 +21,13 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.Properties;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
@@ -33,6 +35,8 @@ import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.RequestPath;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
@@ -44,14 +48,11 @@ import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
@@ -62,21 +63,12 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Sebastien Deleuze
* @author Sam Brannen
* @author Nicolas Labrot
* @author Rossen Stoyanchev
*/
public class CrossOriginTests {
class CrossOriginTests {
private final TestRequestMappingInfoHandlerMapping handlerMapping = new TestRequestMappingInfoHandlerMapping();
private final MockHttpServletRequest request = new MockHttpServletRequest();
private final String optionsHandler = "org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping$HttpOptionsHandler#handle()";
private final String corsPreflightHandler = "org.springframework.web.servlet.handler.AbstractHandlerMapping$PreFlightHandler";
@BeforeEach
@SuppressWarnings("resource")
public void setup() {
@SuppressWarnings("unused")
static Stream<TestRequestMappingInfoHandlerMapping> pathPatternsArguments() {
StaticWebApplicationContext wac = new StaticWebApplicationContext();
Properties props = new Properties();
props.setProperty("myOrigin", "https://example.com");
@@ -84,234 +76,252 @@ public class CrossOriginTests {
wac.registerSingleton("ppc", PropertySourcesPlaceholderConfigurer.class);
wac.refresh();
this.handlerMapping.setRemoveSemicolonContent(false);
wac.getAutowireCapableBeanFactory().initializeBean(this.handlerMapping, "hm");
TestRequestMappingInfoHandlerMapping mapping1 = new TestRequestMappingInfoHandlerMapping();
mapping1.setPatternParser(new PathPatternParser());
wac.getAutowireCapableBeanFactory().initializeBean(mapping1, "mapping1");
TestRequestMappingInfoHandlerMapping mapping2 = new TestRequestMappingInfoHandlerMapping();
wac.getAutowireCapableBeanFactory().initializeBean(mapping2, "mapping2");
return Stream.of(mapping1, mapping2);
}
private final MockHttpServletRequest request = new MockHttpServletRequest();
@BeforeEach
void setup() {
this.request.setMethod("GET");
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain.com/");
}
@Test
public void noAnnotationWithoutOrigin() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest
void noAnnotationWithoutOrigin(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/no");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
assertThat(getCorsConfiguration(chain, false)).isNull();
}
@Test
public void noAnnotationWithAccessControlRequestMethod() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest
void noAnnotationWithAccessControlRequestMethod(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
MockHttpServletRequest request = new MockHttpServletRequest("OPTIONS", "/no");
request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
assertThat(chain.getHandler().toString()).isEqualTo(optionsHandler);
HandlerExecutionChain chain = mapping.getHandler(request);
assertThat(chain).isNotNull();
assertThat(chain.getHandler().toString())
.endsWith("RequestMappingInfoHandlerMapping$HttpOptionsHandler#handle()");
}
@Test
public void noAnnotationWithPreflightRequest() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest
void noAnnotationWithPreflightRequest(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
MockHttpServletRequest request = new MockHttpServletRequest("OPTIONS", "/no");
request.addHeader(HttpHeaders.ORIGIN, "https://domain.com/");
request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
assertThat(chain.getHandler().getClass().getName()).isEqualTo(corsPreflightHandler);
HandlerExecutionChain chain = mapping.getHandler(request);
assertThat(chain).isNotNull();
assertThat(chain.getHandler().getClass().getName()).endsWith("AbstractHandlerMapping$PreFlightHandler");
}
@Test // SPR-12931
public void noAnnotationWithOrigin() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest // SPR-12931
void noAnnotationWithOrigin(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
this.request.setRequestURI("/no");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
assertThat(getCorsConfiguration(chain, false)).isNull();
}
@Test // SPR-12931
public void noAnnotationPostWithOrigin() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest // SPR-12931
void noAnnotationPostWithOrigin(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
this.request.setMethod("POST");
this.request.setRequestURI("/no");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
assertThat(getCorsConfiguration(chain, false)).isNull();
}
@Test
public void defaultAnnotation() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest
void defaultAnnotation(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
this.request.setRequestURI("/default");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertThat(config).isNotNull();
assertThat(config.getAllowedMethods().toArray()).isEqualTo(new String[] {"GET"});
assertThat(config.getAllowedOrigins().toArray()).isEqualTo(new String[] {"*"});
assertThat(config.getAllowedMethods()).containsExactly("GET");
assertThat(config.getAllowedOrigins()).containsExactly("*");
assertThat(config.getAllowCredentials()).isNull();
assertThat(config.getAllowedHeaders().toArray()).isEqualTo(new String[] {"*"});
assertThat(config.getAllowedHeaders()).containsExactly("*");
assertThat(CollectionUtils.isEmpty(config.getExposedHeaders())).isTrue();
assertThat(config.getMaxAge()).isEqualTo(new Long(1800));
}
@Test
public void customized() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest
void customized(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
this.request.setRequestURI("/customized");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertThat(config).isNotNull();
assertThat(config.getAllowedMethods().toArray()).isEqualTo(new String[] {"DELETE"});
assertThat(config.getAllowedOrigins().toArray()).isEqualTo(new String[] {"https://site1.com", "https://site2.com"});
assertThat(config.getAllowedHeaders().toArray()).isEqualTo(new String[] {"header1", "header2"});
assertThat(config.getExposedHeaders().toArray()).isEqualTo(new String[] {"header3", "header4"});
assertThat(config.getAllowedMethods()).containsExactly("DELETE");
assertThat(config.getAllowedOrigins()).containsExactly("https://site1.com", "https://site2.com");
assertThat(config.getAllowedHeaders()).containsExactly("header1", "header2");
assertThat(config.getExposedHeaders()).containsExactly("header3", "header4");
assertThat(config.getMaxAge()).isEqualTo(new Long(123));
assertThat((boolean) config.getAllowCredentials()).isFalse();
assertThat(config.getAllowCredentials()).isFalse();
}
@Test
public void customOriginDefinedViaValueAttribute() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest
void customOriginDefinedViaValueAttribute(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
this.request.setRequestURI("/customOrigin");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertThat(config).isNotNull();
assertThat(config.getAllowedOrigins()).isEqualTo(Arrays.asList("https://example.com"));
assertThat(config.getAllowedOrigins()).isEqualTo(Collections.singletonList("https://example.com"));
assertThat(config.getAllowCredentials()).isNull();
}
@Test
public void customOriginDefinedViaPlaceholder() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest
void customOriginDefinedViaPlaceholder(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
this.request.setRequestURI("/someOrigin");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertThat(config).isNotNull();
assertThat(config.getAllowedOrigins()).isEqualTo(Arrays.asList("https://example.com"));
assertThat(config.getAllowedOrigins()).isEqualTo(Collections.singletonList("https://example.com"));
assertThat(config.getAllowCredentials()).isNull();
}
@Test
public void bogusAllowCredentialsValue() throws Exception {
@PathPatternsParameterizedTest
void bogusAllowCredentialsValue(TestRequestMappingInfoHandlerMapping mapping) {
assertThatIllegalStateException().isThrownBy(() ->
this.handlerMapping.registerHandler(new MethodLevelControllerWithBogusAllowCredentialsValue()))
mapping.registerHandler(new MethodLevelControllerWithBogusAllowCredentialsValue()))
.withMessageContaining("@CrossOrigin's allowCredentials")
.withMessageContaining("current value is [bogus]");
}
@Test
public void classLevel() throws Exception {
this.handlerMapping.registerHandler(new ClassLevelController());
@PathPatternsParameterizedTest
void classLevel(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new ClassLevelController());
this.request.setRequestURI("/foo");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertThat(config).isNotNull();
assertThat(config.getAllowedMethods().toArray()).isEqualTo(new String[] {"GET"});
assertThat(config.getAllowedOrigins().toArray()).isEqualTo(new String[] {"*"});
assertThat((boolean) config.getAllowCredentials()).isFalse();
assertThat(config.getAllowedMethods()).containsExactly("GET");
assertThat(config.getAllowedOrigins()).containsExactly("*");
assertThat(config.getAllowCredentials()).isFalse();
this.request.setRequestURI("/bar");
chain = this.handlerMapping.getHandler(request);
chain = mapping.getHandler(request);
config = getCorsConfiguration(chain, false);
assertThat(config).isNotNull();
assertThat(config.getAllowedMethods().toArray()).isEqualTo(new String[] {"GET"});
assertThat(config.getAllowedOrigins().toArray()).isEqualTo(new String[] {"*"});
assertThat((boolean) config.getAllowCredentials()).isFalse();
assertThat(config.getAllowedMethods()).containsExactly("GET");
assertThat(config.getAllowedOrigins()).containsExactly("*");
assertThat(config.getAllowCredentials()).isFalse();
this.request.setRequestURI("/baz");
chain = this.handlerMapping.getHandler(request);
chain = mapping.getHandler(request);
config = getCorsConfiguration(chain, false);
assertThat(config).isNotNull();
assertThat(config.getAllowedMethods().toArray()).isEqualTo(new String[] {"GET"});
assertThat(config.getAllowedOrigins().toArray()).isEqualTo(new String[] {"*"});
assertThat((boolean) config.getAllowCredentials()).isTrue();
assertThat(config.getAllowedMethods()).containsExactly("GET");
assertThat(config.getAllowedOrigins()).containsExactly("*");
assertThat(config.getAllowCredentials()).isTrue();
}
@Test // SPR-13468
public void classLevelComposedAnnotation() throws Exception {
this.handlerMapping.registerHandler(new ClassLevelMappingWithComposedAnnotation());
@PathPatternsParameterizedTest // SPR-13468
void classLevelComposedAnnotation(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new ClassLevelMappingWithComposedAnnotation());
this.request.setRequestURI("/foo");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertThat(config).isNotNull();
assertThat(config.getAllowedMethods().toArray()).isEqualTo(new String[] {"GET"});
assertThat(config.getAllowedOrigins().toArray()).isEqualTo(new String[] {"http://www.foo.example/"});
assertThat((boolean) config.getAllowCredentials()).isTrue();
assertThat(config.getAllowedMethods()).containsExactly("GET");
assertThat(config.getAllowedOrigins()).containsExactly("http://www.foo.example/");
assertThat(config.getAllowCredentials()).isTrue();
}
@Test // SPR-13468
public void methodLevelComposedAnnotation() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelMappingWithComposedAnnotation());
@PathPatternsParameterizedTest // SPR-13468
void methodLevelComposedAnnotation(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelMappingWithComposedAnnotation());
this.request.setRequestURI("/foo");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, false);
assertThat(config).isNotNull();
assertThat(config.getAllowedMethods().toArray()).isEqualTo(new String[] {"GET"});
assertThat(config.getAllowedOrigins().toArray()).isEqualTo(new String[] {"http://www.foo.example/"});
assertThat((boolean) config.getAllowCredentials()).isTrue();
assertThat(config.getAllowedMethods()).containsExactly("GET");
assertThat(config.getAllowedOrigins()).containsExactly("http://www.foo.example/");
assertThat(config.getAllowCredentials()).isTrue();
}
@Test
public void preFlightRequest() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest
void preFlightRequest(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
this.request.setMethod("OPTIONS");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.setRequestURI("/default");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, true);
assertThat(config).isNotNull();
assertThat(config.getAllowedMethods().toArray()).isEqualTo(new String[] {"GET"});
assertThat(config.getAllowedOrigins().toArray()).isEqualTo(new String[] {"*"});
assertThat(config.getAllowedMethods()).containsExactly("GET");
assertThat(config.getAllowedOrigins()).containsExactly("*");
assertThat(config.getAllowCredentials()).isNull();
assertThat(config.getAllowedHeaders().toArray()).isEqualTo(new String[] {"*"});
assertThat(config.getAllowedHeaders()).containsExactly("*");
assertThat(CollectionUtils.isEmpty(config.getExposedHeaders())).isTrue();
assertThat(config.getMaxAge()).isEqualTo(new Long(1800));
}
@Test
public void ambiguousHeaderPreFlightRequest() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest
void ambiguousHeaderPreFlightRequest(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
this.request.setMethod("OPTIONS");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "header1");
this.request.setRequestURI("/ambiguous-header");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, true);
assertThat(config).isNotNull();
assertThat(config.getAllowedMethods().toArray()).isEqualTo(new String[] {"*"});
assertThat(config.getAllowedOrigins().toArray()).isEqualTo(new String[] {"*"});
assertThat(config.getAllowedHeaders().toArray()).isEqualTo(new String[] {"*"});
assertThat((boolean) config.getAllowCredentials()).isTrue();
assertThat(config.getAllowedMethods()).containsExactly("*");
assertThat(config.getAllowedOrigins()).containsExactly("*");
assertThat(config.getAllowedHeaders()).containsExactly("*");
assertThat(config.getAllowCredentials()).isTrue();
assertThat(CollectionUtils.isEmpty(config.getExposedHeaders())).isTrue();
assertThat(config.getMaxAge()).isNull();
}
@Test
public void ambiguousProducesPreFlightRequest() throws Exception {
this.handlerMapping.registerHandler(new MethodLevelController());
@PathPatternsParameterizedTest
void ambiguousProducesPreFlightRequest(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
mapping.registerHandler(new MethodLevelController());
this.request.setMethod("OPTIONS");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.setRequestURI("/ambiguous-produces");
HandlerExecutionChain chain = this.handlerMapping.getHandler(request);
HandlerExecutionChain chain = mapping.getHandler(request);
CorsConfiguration config = getCorsConfiguration(chain, true);
assertThat(config).isNotNull();
assertThat(config.getAllowedMethods().toArray()).isEqualTo(new String[] {"*"});
assertThat(config.getAllowedOrigins().toArray()).isEqualTo(new String[] {"*"});
assertThat(config.getAllowedHeaders().toArray()).isEqualTo(new String[] {"*"});
assertThat((boolean) config.getAllowCredentials()).isTrue();
assertThat(config.getAllowedMethods()).containsExactly("*");
assertThat(config.getAllowedOrigins()).containsExactly("*");
assertThat(config.getAllowedHeaders()).containsExactly("*");
assertThat(config.getAllowCredentials()).isTrue();
assertThat(CollectionUtils.isEmpty(config.getExposedHeaders())).isTrue();
assertThat(config.getMaxAge()).isNull();
}
@Test
public void preFlightRequestWithoutRequestMethodHeader() throws Exception {
@PathPatternsParameterizedTest
void preFlightRequestWithoutRequestMethodHeader(TestRequestMappingInfoHandlerMapping mapping) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("OPTIONS", "/default");
request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
assertThat(this.handlerMapping.getHandler(request)).isNull();
assertThat(mapping.getHandler(request)).isNull();
}
private CorsConfiguration getCorsConfiguration(HandlerExecutionChain chain, boolean isPreFlightRequest) {
@Nullable
private CorsConfiguration getCorsConfiguration(@Nullable HandlerExecutionChain chain, boolean isPreFlightRequest) {
assertThat(chain).isNotNull();
if (isPreFlightRequest) {
Object handler = chain.getHandler();
assertThat(handler.getClass().getSimpleName().equals("PreFlightHandler")).isTrue();
@@ -334,6 +344,7 @@ public class CrossOriginTests {
@Controller
@SuppressWarnings("unused")
private static class MethodLevelController {
@GetMapping("/no")
@@ -399,6 +410,7 @@ public class CrossOriginTests {
@Controller
@SuppressWarnings("unused")
private static class MethodLevelControllerWithBogusAllowCredentialsValue {
@CrossOrigin(allowCredentials = "bogus")
@@ -462,7 +474,7 @@ public class CrossOriginTests {
private static class TestRequestMappingInfoHandlerMapping extends RequestMappingHandlerMapping {
public void registerHandler(Object handler) {
void registerHandler(Object handler) {
super.detectHandlerMethods(handler);
}
@@ -475,18 +487,33 @@ public class CrossOriginTests {
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMapping annotation = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
if (annotation != null) {
return new RequestMappingInfo(
new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher(), true, true),
new RequestMethodsRequestCondition(annotation.method()),
new ParamsRequestCondition(annotation.params()),
new HeadersRequestCondition(annotation.headers()),
new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
new ProducesRequestCondition(annotation.produces(), annotation.headers()), null);
RequestMappingInfo.BuilderConfiguration options = new RequestMappingInfo.BuilderConfiguration();
if (getPatternParser() != null) {
options.setPatternParser(getPatternParser());
}
return RequestMappingInfo.paths(annotation.value())
.methods(annotation.method())
.params(annotation.params())
.headers(annotation.headers())
.consumes(annotation.consumes())
.produces(annotation.produces())
.options(options)
.build();
}
else {
return null;
}
}
@Override
protected String initLookupPath(HttpServletRequest request) {
// At runtime this is done by the DispatcherServlet
if (getPatternParser() != null) {
RequestPath requestPath = ServletRequestPathUtils.parseAndCache(request);
return requestPath.pathWithinApplication().value();
}
return super.initLookupPath(request);
}
}
}

View File

@@ -26,8 +26,10 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.provider.Arguments;
import org.springframework.core.annotation.AliasFor;
import org.springframework.http.MediaType;
@@ -46,9 +48,12 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.method.HandlerTypePredicate;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -61,41 +66,49 @@ import static org.mockito.Mockito.mock;
*/
public class RequestMappingHandlerMappingTests {
private final StaticWebApplicationContext wac = new StaticWebApplicationContext();
@SuppressWarnings("unused")
static Stream<Arguments> pathPatternsArguments() {
RequestMappingHandlerMapping mapping1 = new RequestMappingHandlerMapping();
StaticWebApplicationContext wac1 = new StaticWebApplicationContext();
mapping1.setPatternParser(new PathPatternParser());
mapping1.setApplicationContext(wac1);
private final RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
{
this.handlerMapping.setApplicationContext(wac);
RequestMappingHandlerMapping mapping2 = new RequestMappingHandlerMapping();
StaticWebApplicationContext wac2 = new StaticWebApplicationContext();
mapping2.setApplicationContext(wac2);
return Stream.of(Arguments.of(mapping1, wac1), Arguments.of(mapping2, wac2));
}
@Test
public void useRegisteredSuffixPatternMatch() {
assertThat(this.handlerMapping.useSuffixPatternMatch()).isFalse();
assertThat(this.handlerMapping.useRegisteredSuffixPatternMatch()).isFalse();
void useRegisteredSuffixPatternMatch() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setApplicationContext(new StaticWebApplicationContext());
Map<String, MediaType> fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON);
PathExtensionContentNegotiationStrategy strategy = new PathExtensionContentNegotiationStrategy(fileExtensions);
ContentNegotiationManager manager = new ContentNegotiationManager(strategy);
this.handlerMapping.setContentNegotiationManager(manager);
this.handlerMapping.setUseRegisteredSuffixPatternMatch(true);
this.handlerMapping.afterPropertiesSet();
handlerMapping.setContentNegotiationManager(manager);
handlerMapping.setUseRegisteredSuffixPatternMatch(true);
handlerMapping.afterPropertiesSet();
assertThat(this.handlerMapping.useSuffixPatternMatch()).isTrue();
assertThat(this.handlerMapping.useRegisteredSuffixPatternMatch()).isTrue();
assertThat(this.handlerMapping.getFileExtensions()).isEqualTo(Collections.singletonList("json"));
assertThat(handlerMapping.useSuffixPatternMatch()).isTrue();
assertThat(handlerMapping.useRegisteredSuffixPatternMatch()).isTrue();
assertThat(handlerMapping.getFileExtensions()).isEqualTo(Collections.singletonList("json"));
}
@Test
public void useRegisteredSuffixPatternMatchInitialization() {
void useRegisteredSuffixPatternMatchInitialization() {
Map<String, MediaType> fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON);
PathExtensionContentNegotiationStrategy strategy = new PathExtensionContentNegotiationStrategy(fileExtensions);
ContentNegotiationManager manager = new ContentNegotiationManager(strategy);
final Set<String> extensions = new HashSet<>();
RequestMappingHandlerMapping hm = new RequestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping() {
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
extensions.addAll(getFileExtensions());
@@ -103,76 +116,99 @@ public class RequestMappingHandlerMappingTests {
}
};
StaticWebApplicationContext wac = new StaticWebApplicationContext();
wac.registerSingleton("testController", ComposedAnnotationController.class);
wac.refresh();
hm.setContentNegotiationManager(manager);
hm.setUseRegisteredSuffixPatternMatch(true);
hm.setApplicationContext(wac);
hm.afterPropertiesSet();
mapping.setContentNegotiationManager(manager);
mapping.setUseRegisteredSuffixPatternMatch(true);
mapping.setApplicationContext(wac);
mapping.afterPropertiesSet();
assertThat(extensions).isEqualTo(Collections.singleton("json"));
}
@Test
public void useSuffixPatternMatch() {
assertThat(this.handlerMapping.useSuffixPatternMatch()).isFalse();
void suffixPatternMatchSettings() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
this.handlerMapping.setUseRegisteredSuffixPatternMatch(false);
assertThat(this.handlerMapping.useSuffixPatternMatch())
.as("'false' registeredSuffixPatternMatch shouldn't impact suffixPatternMatch").isFalse();
assertThat(handlerMapping.useSuffixPatternMatch()).isFalse();
assertThat(handlerMapping.useRegisteredSuffixPatternMatch()).isFalse();
this.handlerMapping.setUseRegisteredSuffixPatternMatch(true);
assertThat(this.handlerMapping.useSuffixPatternMatch())
.as("'true' registeredSuffixPatternMatch should enable suffixPatternMatch").isTrue();
handlerMapping.setUseRegisteredSuffixPatternMatch(false);
assertThat(handlerMapping.useSuffixPatternMatch())
.as("'false' registeredSuffixPatternMatch shouldn't impact suffixPatternMatch")
.isFalse();
handlerMapping.setUseRegisteredSuffixPatternMatch(true);
assertThat(handlerMapping.useSuffixPatternMatch())
.as("'true' registeredSuffixPatternMatch should enable suffixPatternMatch")
.isTrue();
}
@Test
public void resolveEmbeddedValuesInPatterns() {
this.handlerMapping.setEmbeddedValueResolver(
@PathPatternsParameterizedTest
void resolveEmbeddedValuesInPatterns(RequestMappingHandlerMapping mapping) {
mapping.setEmbeddedValueResolver(
value -> "/${pattern}/bar".equals(value) ? "/foo/bar" : value
);
String[] patterns = new String[] { "/foo", "/${pattern}/bar" };
String[] result = this.handlerMapping.resolveEmbeddedValuesInPatterns(patterns);
String[] result = mapping.resolveEmbeddedValuesInPatterns(patterns);
assertThat(result).isEqualTo(new String[] { "/foo", "/foo/bar" });
}
@Test
public void pathPrefix() throws NoSuchMethodException {
this.handlerMapping.setEmbeddedValueResolver(value -> "/${prefix}".equals(value) ? "/api" : value);
this.handlerMapping.setPathPrefixes(Collections.singletonMap(
@PathPatternsParameterizedTest
void pathPrefix(RequestMappingHandlerMapping mapping) throws Exception {
mapping.setEmbeddedValueResolver(value -> "/${prefix}".equals(value) ? "/api" : value);
mapping.setPathPrefixes(Collections.singletonMap(
"/${prefix}", HandlerTypePredicate.forAnnotation(RestController.class)));
mapping.afterPropertiesSet();
Method method = UserController.class.getMethod("getUser");
RequestMappingInfo info = this.handlerMapping.getMappingForMethod(method, UserController.class);
RequestMappingInfo info = mapping.getMappingForMethod(method, UserController.class);
assertThat(info).isNotNull();
assertThat(info.getPatternsCondition().getPatterns()).isEqualTo(Collections.singleton("/api/user/{id}"));
assertThat(info.getPatternValues()).isEqualTo(Collections.singleton("/api/user/{id}"));
}
@Test // gh-23907
public void pathPrefixPreservesPathMatchingSettings() throws NoSuchMethodException {
this.handlerMapping.setUseSuffixPatternMatch(false);
this.handlerMapping.setPathPrefixes(Collections.singletonMap("/api", HandlerTypePredicate.forAnyHandlerType()));
this.handlerMapping.afterPropertiesSet();
@PathPatternsParameterizedTest // gh-23907
void pathPrefixPreservesPathMatchingSettings(RequestMappingHandlerMapping mapping) throws Exception {
mapping.setPathPrefixes(Collections.singletonMap("/api", HandlerTypePredicate.forAnyHandlerType()));
mapping.afterPropertiesSet();
Method method = ComposedAnnotationController.class.getMethod("get");
RequestMappingInfo info = this.handlerMapping.getMappingForMethod(method, ComposedAnnotationController.class);
RequestMappingInfo info = mapping.getMappingForMethod(method, ComposedAnnotationController.class);
assertThat(info).isNotNull();
assertThat(info.getActivePatternsCondition()).isNotNull();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api/get");
assertThat(info.getPatternsCondition().getMatchingCondition(request)).isNotNull();
initRequestPath(mapping, request);
assertThat(info.getActivePatternsCondition().getMatchingCondition(request)).isNotNull();
request = new MockHttpServletRequest("GET", "/api/get.pdf");
assertThat(info.getPatternsCondition().getMatchingCondition(request)).isNull();
initRequestPath(mapping, request);
assertThat(info.getActivePatternsCondition().getMatchingCondition(request)).isNull();
}
@Test
public void resolveRequestMappingViaComposedAnnotation() throws Exception {
RequestMappingInfo info = assertComposedAnnotationMapping("postJson", "/postJson", RequestMethod.POST);
private void initRequestPath(RequestMappingHandlerMapping mapping, MockHttpServletRequest request) {
PathPatternParser parser = mapping.getPatternParser();
if (parser != null) {
ServletRequestPathUtils.parseAndCache(request);
}
else {
mapping.getUrlPathHelper().resolveAndCacheLookupPath(request);
}
}
@PathPatternsParameterizedTest
void resolveRequestMappingViaComposedAnnotation(RequestMappingHandlerMapping mapping) {
RequestMappingInfo info = assertComposedAnnotationMapping(
mapping, "postJson", "/postJson", RequestMethod.POST);
assertThat(info.getConsumesCondition().getConsumableMediaTypes().iterator().next().toString())
.isEqualTo(MediaType.APPLICATION_JSON_VALUE);
@@ -181,68 +217,72 @@ public class RequestMappingHandlerMappingTests {
}
@Test // SPR-14988
public void getMappingOverridesConsumesFromTypeLevelAnnotation() throws Exception {
void getMappingOverridesConsumesFromTypeLevelAnnotation() throws Exception {
RequestMappingInfo requestMappingInfo = assertComposedAnnotationMapping(RequestMethod.POST);
ConsumesRequestCondition condition = requestMappingInfo.getConsumesCondition();
assertThat(condition.getConsumableMediaTypes()).isEqualTo(Collections.singleton(MediaType.APPLICATION_XML));
}
@Test // gh-22010
public void consumesWithOptionalRequestBody() {
this.wac.registerSingleton("testController", ComposedAnnotationController.class);
this.wac.refresh();
this.handlerMapping.afterPropertiesSet();
RequestMappingInfo info = this.handlerMapping.getHandlerMethods().keySet().stream()
.filter(i -> i.getPatternsCondition().getPatterns().equals(Collections.singleton("/post")))
@PathPatternsParameterizedTest // gh-22010
void consumesWithOptionalRequestBody(RequestMappingHandlerMapping mapping, StaticWebApplicationContext wac) {
wac.registerSingleton("testController", ComposedAnnotationController.class);
wac.refresh();
mapping.afterPropertiesSet();
RequestMappingInfo result = mapping.getHandlerMethods().keySet().stream()
.filter(info -> info.getPatternValues().equals(Collections.singleton("/post")))
.findFirst()
.orElseThrow(() -> new AssertionError("No /post"));
assertThat(info.getConsumesCondition().isBodyRequired()).isFalse();
assertThat(result.getConsumesCondition().isBodyRequired()).isFalse();
}
@Test
public void getMapping() throws Exception {
void getMapping() throws Exception {
assertComposedAnnotationMapping(RequestMethod.GET);
}
@Test
public void postMapping() throws Exception {
void postMapping() throws Exception {
assertComposedAnnotationMapping(RequestMethod.POST);
}
@Test
public void putMapping() throws Exception {
void putMapping() throws Exception {
assertComposedAnnotationMapping(RequestMethod.PUT);
}
@Test
public void deleteMapping() throws Exception {
void deleteMapping() throws Exception {
assertComposedAnnotationMapping(RequestMethod.DELETE);
}
@Test
public void patchMapping() throws Exception {
void patchMapping() throws Exception {
assertComposedAnnotationMapping(RequestMethod.PATCH);
}
private RequestMappingInfo assertComposedAnnotationMapping(RequestMethod requestMethod) throws Exception {
RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
mapping.setApplicationContext(new StaticWebApplicationContext());
String methodName = requestMethod.name().toLowerCase();
String path = "/" + methodName;
return assertComposedAnnotationMapping(methodName, path, requestMethod);
return assertComposedAnnotationMapping(mapping, methodName, path, requestMethod);
}
private RequestMappingInfo assertComposedAnnotationMapping(String methodName, String path,
RequestMethod requestMethod) throws Exception {
private RequestMappingInfo assertComposedAnnotationMapping(
RequestMappingHandlerMapping mapping, String methodName, String path, RequestMethod requestMethod) {
Class<?> clazz = ComposedAnnotationController.class;
Method method = ClassUtils.getMethod(clazz, methodName, (Class<?>[]) null);
RequestMappingInfo info = this.handlerMapping.getMappingForMethod(method, clazz);
RequestMappingInfo info = mapping.getMappingForMethod(method, clazz);
assertThat(info).isNotNull();
Set<String> paths = info.getPatternsCondition().getPatterns();
Set<String> paths = info.getPatternValues();
assertThat(paths.size()).isEqualTo(1);
assertThat(paths.iterator().next()).isEqualTo(path);

View File

@@ -19,19 +19,17 @@ package org.springframework.web.servlet.mvc.method.annotation;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
@@ -45,6 +43,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
import org.springframework.web.servlet.view.AbstractView;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
@@ -57,9 +56,15 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class UriTemplateServletAnnotationControllerHandlerMethodTests extends AbstractServletHandlerMethodTests {
@Test
public void simple() throws Exception {
initServletWithControllers(SimpleUriTemplateController.class);
@SuppressWarnings("unused")
static Stream<Boolean> pathPatternsArguments() {
return Stream.of(true, false);
}
@PathPatternsParameterizedTest
void simple(boolean usePathPatterns) throws Exception {
initDispatcherServlet(SimpleUriTemplateController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -67,9 +72,9 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getContentAsString()).isEqualTo("test-42-7");
}
@Test
public void multiple() throws Exception {
initServletWithControllers(MultipleUriTemplateController.class);
@PathPatternsParameterizedTest
void multiple(boolean usePathPatterns) throws Exception {
initDispatcherServlet(MultipleUriTemplateController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42;q=24/bookings/21-other;q=12");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -78,29 +83,29 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getContentAsString()).isEqualTo("test-42-q24-21-other-q12");
}
@Test
public void pathVarsInModel() throws Exception {
@PathPatternsParameterizedTest
void pathVarsInModel(boolean usePathPatterns) throws Exception {
final Map<String, Object> pathVars = new HashMap<>();
pathVars.put("hotel", "42");
pathVars.put("booking", 21);
pathVars.put("other", "other");
WebApplicationContext wac = initServlet(context -> {
WebApplicationContext wac = initDispatcherServlet(ViewRenderingController.class, usePathPatterns, context -> {
RootBeanDefinition beanDef = new RootBeanDefinition(ModelValidatingViewResolver.class);
beanDef.getConstructorArgumentValues().addGenericArgumentValue(pathVars);
context.registerBeanDefinition("viewResolver", beanDef);
}, ViewRenderingController.class);
});
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42;q=1,2/bookings/21-other;q=3;r=R");
HttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42;q=1,2/bookings/21-other;q=3;r=R");
getServlet().service(request, new MockHttpServletResponse());
ModelValidatingViewResolver resolver = wac.getBean(ModelValidatingViewResolver.class);
assertThat(resolver.validatedAttrCount).isEqualTo(3);
}
@Test
public void binding() throws Exception {
initServletWithControllers(BindingUriTemplateController.class);
@PathPatternsParameterizedTest
void binding(boolean usePathPatterns) throws Exception {
initDispatcherServlet(BindingUriTemplateController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/dates/2008-11-18");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -112,16 +117,16 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
getServlet().service(request, response);
assertThat(response.getStatus()).isEqualTo(400);
initServletWithControllers(NonBindingUriTemplateController.class);
initDispatcherServlet(NonBindingUriTemplateController.class, usePathPatterns);
request = new MockHttpServletRequest("GET", "/hotels/42/dates/2008-foo-bar");
response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getStatus()).isEqualTo(500);
}
@Test
public void ambiguous() throws Exception {
initServletWithControllers(AmbiguousUriTemplateController.class);
@PathPatternsParameterizedTest
void ambiguous(boolean usePathPatterns) throws Exception {
initDispatcherServlet(AmbiguousUriTemplateController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/new");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -129,9 +134,9 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getContentAsString()).isEqualTo("specific");
}
@Test
public void relative() throws Exception {
initServletWithControllers(RelativePathUriTemplateController.class);
@PathPatternsParameterizedTest
void relative(boolean usePathPatterns) throws Exception {
initDispatcherServlet(RelativePathUriTemplateController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -139,24 +144,27 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getContentAsString()).isEqualTo("test-42-21");
}
@Test
public void extension() throws Exception {
initServlet(wac -> {
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
mappingDef.getPropertyValues().add("removeSemicolonContent", "false");
wac.registerBeanDefinition("handlerMapping", mappingDef);
}, SimpleUriTemplateController.class);
@PathPatternsParameterizedTest
void extension(boolean usePathPatterns) throws Exception {
initDispatcherServlet(SimpleUriTemplateController.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
mappingDef.getPropertyValues().add("removeSemicolonContent", "false");
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
});
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42;jsessionid=c0o7fszeb1;q=24.xml");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getContentAsString()).isEqualTo("test-42-24");
assertThat(response.getContentAsString())
.isEqualTo(!usePathPatterns ? "test-42-24" : "test-42-24.xml");
}
@Test
public void typeConversionError() throws Exception {
initServletWithControllers(SimpleUriTemplateController.class);
@PathPatternsParameterizedTest
void typeConversionError(boolean usePathPatterns) throws Exception {
initDispatcherServlet(SimpleUriTemplateController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo.xml");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -164,9 +172,9 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getStatus()).as("Invalid response status code").isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
}
@Test
public void explicitSubPath() throws Exception {
initServletWithControllers(ExplicitSubPathController.class);
@PathPatternsParameterizedTest
void explicitSubPath(boolean usePathPatterns) throws Exception {
initDispatcherServlet(ExplicitSubPathController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -174,9 +182,9 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getContentAsString()).isEqualTo("test-42");
}
@Test
public void implicitSubPath() throws Exception {
initServletWithControllers(ImplicitSubPathController.class);
@PathPatternsParameterizedTest
void implicitSubPath(boolean usePathPatterns) throws Exception {
initDispatcherServlet(ImplicitSubPathController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -184,9 +192,9 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getContentAsString()).isEqualTo("test-42");
}
@Test
public void crud() throws Exception {
initServletWithControllers(CrudController.class);
@PathPatternsParameterizedTest
void crud(boolean usePathPatterns) throws Exception {
initDispatcherServlet(CrudController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -224,9 +232,9 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getContentAsString()).isEqualTo("remove-42");
}
@Test
public void methodNotSupported() throws Exception {
initServletWithControllers(MethodNotAllowedController.class);
@PathPatternsParameterizedTest
void methodNotSupported(boolean usePathPatterns) throws Exception {
initDispatcherServlet(MethodNotAllowedController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/1");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -249,9 +257,9 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getStatus()).isEqualTo(405);
}
@Test
public void multiPaths() throws Exception {
initServletWithControllers(MultiPathController.class);
@PathPatternsParameterizedTest
void multiPaths(boolean usePathPatterns) throws Exception {
initDispatcherServlet(MultiPathController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/category/page/5");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -259,20 +267,21 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getContentAsString()).isEqualTo("handle4-page-5");
}
@Test
public void customRegex() throws Exception {
initServletWithControllers(CustomRegexController.class);
@PathPatternsParameterizedTest
void customRegex(boolean usePathPatterns) throws Exception {
initDispatcherServlet(CustomRegexController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42;q=1;q=2");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.getContentAsString()).isEqualTo("test-42-;q=1;q=2-[1, 2]");
assertThat(response.getContentAsString())
.isEqualTo(!usePathPatterns ? "test-42-;q=1;q=2-[1, 2]" : "test-42--[1, 2]");
}
@Test // gh-11306
public void menuTree() throws Exception {
initServletWithControllers(MenuTreeController.class);
@PathPatternsParameterizedTest // gh-11306
void menuTree(boolean usePathPatterns) throws Exception {
initDispatcherServlet(MenuTreeController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/book/menu/type/M5");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -280,9 +289,9 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getContentAsString()).isEqualTo("M5");
}
@Test // gh-11542
public void variableNames() throws Exception {
initServletWithControllers(VariableNamesController.class);
@PathPatternsParameterizedTest // gh-11542
void variableNames(boolean usePathPatterns) throws Exception {
initDispatcherServlet(VariableNamesController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test/foo");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -295,23 +304,25 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
assertThat(response.getContentAsString()).isEqualTo("bar-bar");
}
@Test // gh-13187
public void variableNamesWithUrlExtension() throws Exception {
initServlet(wac -> {
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
wac.registerBeanDefinition("handlerMapping", mappingDef);
}, VariableNamesController.class);
@PathPatternsParameterizedTest // gh-13187
void variableNamesWithUrlExtension(boolean usePathPatterns) throws Exception {
initDispatcherServlet(VariableNamesController.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
});
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test/foo.json");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getContentAsString()).isEqualTo("foo-foo");
assertThat(response.getContentAsString()).isEqualTo(!usePathPatterns ? "foo-foo" : "foo-foo.json");
}
@Test // gh-11643
public void doIt() throws Exception {
initServletWithControllers(Spr6978Controller.class);
@PathPatternsParameterizedTest // gh-11643
void doIt(boolean usePathPatterns) throws Exception {
initDispatcherServlet(Spr6978Controller.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/100");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -344,7 +355,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class SimpleUriTemplateController {
@RequestMapping("/{root}")
public void handle(@PathVariable("root") int root, @MatrixVariable(required=false, defaultValue="7") int q,
void handle(@PathVariable("root") int root, @MatrixVariable(required=false, defaultValue="7") String q,
Writer writer) throws IOException {
assertThat(root).as("Invalid path variable value").isEqualTo(42);
@@ -357,7 +368,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class MultipleUriTemplateController {
@RequestMapping("/hotels/{hotel}/bookings/{booking}-{other}")
public void handle(@PathVariable("hotel") String hotel,
void handle(@PathVariable("hotel") String hotel,
@PathVariable int booking,
@PathVariable String other,
@MatrixVariable(name = "q", pathVar = "hotel") int qHotel,
@@ -374,12 +385,12 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class ViewRenderingController {
@RequestMapping("/hotels/{hotel}/bookings/{booking}-{other}")
public void handle(@PathVariable("hotel") String hotel, @PathVariable int booking,
void handle(@PathVariable("hotel") String hotel, @PathVariable int booking,
@PathVariable String other, @MatrixVariable MultiValueMap<String, String> params) {
assertThat(hotel).as("Invalid path variable value").isEqualTo("42");
assertThat(booking).as("Invalid path variable value").isEqualTo(21);
assertThat(params.get("q")).isEqualTo(Arrays.asList("1", "2", "3"));
assertThat(params.get("q")).containsExactlyInAnyOrder("1", "2", "3");
assertThat(params.getFirst("r")).isEqualTo("R");
}
@@ -389,7 +400,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class BindingUriTemplateController {
@InitBinder
public void initBinder(WebDataBinder binder, @PathVariable("hotel") String hotel) {
void initBinder(WebDataBinder binder, @PathVariable("hotel") String hotel) {
assertThat(hotel).as("Invalid path variable value").isEqualTo("42");
binder.initBeanPropertyAccess();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
@@ -398,7 +409,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
}
@RequestMapping("/hotels/{hotel}/dates/{date}")
public void handle(@PathVariable("hotel") String hotel, @PathVariable Date date, Writer writer)
void handle(@PathVariable("hotel") String hotel, @PathVariable Date date, Writer writer)
throws IOException {
assertThat(hotel).as("Invalid path variable value").isEqualTo("42");
assertThat(date).as("Invalid path variable value").isEqualTo(new GregorianCalendar(2008, 10, 18).getTime());
@@ -410,7 +421,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class NonBindingUriTemplateController {
@RequestMapping("/hotels/{hotel}/dates/{date}")
public void handle(@PathVariable("hotel") String hotel, @PathVariable Date date, Writer writer)
void handle(@PathVariable("hotel") String hotel, @PathVariable Date date, Writer writer)
throws IOException {
}
@@ -421,7 +432,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class RelativePathUriTemplateController {
@RequestMapping("bookings/{booking}")
public void handle(@PathVariable("hotel") String hotel, @PathVariable int booking, Writer writer)
void handle(@PathVariable("hotel") String hotel, @PathVariable int booking, Writer writer)
throws IOException {
assertThat(hotel).as("Invalid path variable value").isEqualTo("42");
assertThat(booking).as("Invalid path variable value").isEqualTo(21);
@@ -435,18 +446,18 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class AmbiguousUriTemplateController {
@RequestMapping("/{hotel}")
public void handleVars(@PathVariable("hotel") String hotel, Writer writer) throws IOException {
void handleVars(@PathVariable("hotel") String hotel, Writer writer) throws IOException {
assertThat(hotel).as("Invalid path variable value").isEqualTo("42");
writer.write("variables");
}
@RequestMapping("/new")
public void handleSpecific(Writer writer) throws IOException {
void handleSpecific(Writer writer) throws IOException {
writer.write("specific");
}
@RequestMapping("/*")
public void handleWildCard(Writer writer) throws IOException {
void handleWildCard(Writer writer) throws IOException {
writer.write("wildcard");
}
@@ -457,7 +468,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class ExplicitSubPathController {
@RequestMapping("{hotel}")
public void handleHotel(@PathVariable String hotel, Writer writer) throws IOException {
void handleHotel(@PathVariable String hotel, Writer writer) throws IOException {
writer.write("test-" + hotel);
}
@@ -468,7 +479,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class ImplicitSubPathController {
@RequestMapping("{hotel}")
public void handleHotel(@PathVariable String hotel, Writer writer) throws IOException {
void handleHotel(@PathVariable String hotel, Writer writer) throws IOException {
writer.write("test-" + hotel);
}
}
@@ -477,7 +488,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class CustomRegexController {
@RequestMapping("/{root:\\d+}{params}")
public void handle(@PathVariable("root") int root, @PathVariable("params") String paramString,
void handle(@PathVariable("root") int root, @PathVariable("params") String paramString,
@MatrixVariable List<Integer> q, Writer writer) throws IOException {
assertThat(root).as("Invalid path variable value").isEqualTo(42);
@@ -489,7 +500,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class DoubleController {
@RequestMapping("/lat/{latitude}/long/{longitude}")
public void testLatLong(@PathVariable Double latitude, @PathVariable Double longitude, Writer writer)
void testLatLong(@PathVariable Double latitude, @PathVariable Double longitude, Writer writer)
throws IOException {
writer.write("latitude-" + latitude + "-longitude-" + longitude);
}
@@ -501,27 +512,27 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class CrudController {
@RequestMapping(method = RequestMethod.GET)
public void list(Writer writer) throws IOException {
void list(Writer writer) throws IOException {
writer.write("list");
}
@RequestMapping(method = RequestMethod.POST)
public void create(Writer writer) throws IOException {
void create(Writer writer) throws IOException {
writer.write("create");
}
@RequestMapping(value = "/{hotel}", method = RequestMethod.GET)
public void show(@PathVariable String hotel, Writer writer) throws IOException {
void show(@PathVariable String hotel, Writer writer) throws IOException {
writer.write("show-" + hotel);
}
@RequestMapping(value = "{hotel}", method = RequestMethod.PUT)
public void createOrUpdate(@PathVariable String hotel, Writer writer) throws IOException {
void createOrUpdate(@PathVariable String hotel, Writer writer) throws IOException {
writer.write("createOrUpdate-" + hotel);
}
@RequestMapping(value = "{hotel}", method = RequestMethod.DELETE)
public void remove(@PathVariable String hotel, Writer writer) throws IOException {
void remove(@PathVariable String hotel, Writer writer) throws IOException {
writer.write("remove-" + hotel);
}
@@ -532,19 +543,19 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class MethodNotAllowedController {
@RequestMapping(method = RequestMethod.GET)
public void list(Writer writer) {
void list(Writer writer) {
}
@RequestMapping(method = RequestMethod.GET, value = "{hotelId}")
public void show(@PathVariable long hotelId, Writer writer) {
void show(@PathVariable long hotelId, Writer writer) {
}
@RequestMapping(method = RequestMethod.PUT, value = "{hotelId}")
public void createOrUpdate(@PathVariable long hotelId, Writer writer) {
void createOrUpdate(@PathVariable long hotelId, Writer writer) {
}
@RequestMapping(method = RequestMethod.DELETE, value = "/{hotelId}")
public void remove(@PathVariable long hotelId, Writer writer) {
void remove(@PathVariable long hotelId, Writer writer) {
}
}
@@ -553,25 +564,25 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class MultiPathController {
@RequestMapping(value = {"/{category}/page/{page}", "/*/{category}/page/{page}"})
public void category(@PathVariable String category, @PathVariable int page, Writer writer) throws IOException {
void category(@PathVariable String category, @PathVariable int page, Writer writer) throws IOException {
writer.write("handle1-");
writer.write("category-" + category);
writer.write("page-" + page);
}
@RequestMapping(value = {"/{category}", "/*/{category}"})
public void category(@PathVariable String category, Writer writer) throws IOException {
void category(@PathVariable String category, Writer writer) throws IOException {
writer.write("handle2-");
writer.write("category-" + category);
}
@RequestMapping(value = {""})
public void category(Writer writer) throws IOException {
void category(Writer writer) throws IOException {
writer.write("handle3");
}
@RequestMapping(value = {"/page/{page}"})
public void category(@PathVariable int page, Writer writer) throws IOException {
void category(@PathVariable int page, Writer writer) throws IOException {
writer.write("handle4-");
writer.write("page-" + page);
}
@@ -583,7 +594,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class MenuTreeController {
@RequestMapping("type/{var}")
public void getFirstLevelFunctionNodes(@PathVariable("var") String var, Writer writer) throws IOException {
void getFirstLevelFunctionNodes(@PathVariable("var") String var, Writer writer) throws IOException {
writer.write(var);
}
}
@@ -593,12 +604,12 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class VariableNamesController {
@RequestMapping(value = "/{foo}", method=RequestMethod.GET)
public void foo(@PathVariable String foo, Writer writer) throws IOException {
void foo(@PathVariable String foo, Writer writer) throws IOException {
writer.write("foo-" + foo);
}
@RequestMapping(value = "/{bar}", method=RequestMethod.DELETE)
public void bar(@PathVariable String bar, Writer writer) throws IOException {
void bar(@PathVariable String bar, Writer writer) throws IOException {
writer.write("bar-" + bar);
}
}
@@ -607,18 +618,18 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
public static class Spr6978Controller {
@RequestMapping(value = "/{type}/{id}", method = RequestMethod.GET)
public void loadEntity(@PathVariable final String type, @PathVariable final long id, Writer writer)
void loadEntity(@PathVariable final String type, @PathVariable final long id, Writer writer)
throws IOException {
writer.write("loadEntity:" + type + ":" + id);
}
@RequestMapping(value = "/module/{id}", method = RequestMethod.GET)
public void loadModule(@PathVariable final long id, Writer writer) throws IOException {
void loadModule(@PathVariable final long id, Writer writer) throws IOException {
writer.write("loadModule:" + id);
}
@RequestMapping(value = "/{type}/{id}", method = RequestMethod.POST)
public void publish(@PathVariable final String type, @PathVariable final long id, Writer writer)
void publish(@PathVariable final String type, @PathVariable final long id, Writer writer)
throws IOException {
writer.write("publish:" + type + ":" + id);
}
@@ -655,12 +666,12 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab
}
// @Disabled("ControllerClassNameHandlerMapping")
// public void controllerClassName() throws Exception {
// void controllerClassName() throws Exception {
// @Disabled("useDefaultSuffixPattern property not supported")
// public void doubles() throws Exception {
// void doubles() throws Exception {
// @Disabled("useDefaultSuffixPattern property not supported")
// public void noDefaultSuffixPattern() throws Exception {
// void noDefaultSuffixPattern() throws Exception {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -16,9 +16,11 @@
package org.springframework.web.servlet.view;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.function.Function;
import java.util.stream.Stream;
import org.springframework.web.servlet.handler.PathPatternsParameterizedTest;
import org.springframework.web.servlet.handler.PathPatternsTestUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import static org.assertj.core.api.Assertions.assertThat;
@@ -31,110 +33,99 @@ public class DefaultRequestToViewNameTranslatorTests {
private static final String VIEW_NAME = "apple";
private static final String EXTENSION = ".html";
private static final String CONTEXT_PATH = "/sundays";
private DefaultRequestToViewNameTranslator translator;
private MockHttpServletRequest request;
private final DefaultRequestToViewNameTranslator translator = new DefaultRequestToViewNameTranslator();
@BeforeEach
public void setUp() {
this.translator = new DefaultRequestToViewNameTranslator();
this.request = new MockHttpServletRequest();
this.request.setContextPath(CONTEXT_PATH);
@SuppressWarnings("unused")
private static Stream<Function<String, MockHttpServletRequest>> pathPatternsArguments() {
return PathPatternsTestUtils.requestArguments("/sundays");
}
@Test
public void testGetViewNameLeavesLeadingSlashIfSoConfigured() {
request.setRequestURI(CONTEXT_PATH + "/" + VIEW_NAME + "/");
@PathPatternsParameterizedTest
void testGetViewNameLeavesLeadingSlashIfSoConfigured(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + "/");
this.translator.setStripLeadingSlash(false);
assertViewName("/" + VIEW_NAME);
assertViewName(request, "/" + VIEW_NAME);
}
@Test
public void testGetViewNameLeavesTrailingSlashIfSoConfigured() {
request.setRequestURI(CONTEXT_PATH + "/" + VIEW_NAME + "/");
@PathPatternsParameterizedTest
void testGetViewNameLeavesTrailingSlashIfSoConfigured(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + "/");
this.translator.setStripTrailingSlash(false);
assertViewName(VIEW_NAME + "/");
assertViewName(request, VIEW_NAME + "/");
}
@Test
public void testGetViewNameLeavesExtensionIfSoConfigured() {
request.setRequestURI(CONTEXT_PATH + "/" + VIEW_NAME + EXTENSION);
@PathPatternsParameterizedTest
void testGetViewNameLeavesExtensionIfSoConfigured(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + EXTENSION);
this.translator.setStripExtension(false);
assertViewName(VIEW_NAME + EXTENSION);
assertViewName(request, VIEW_NAME + EXTENSION);
}
@Test
public void testGetViewNameWithDefaultConfiguration() {
request.setRequestURI(CONTEXT_PATH + VIEW_NAME + EXTENSION);
assertViewName(VIEW_NAME);
@PathPatternsParameterizedTest
void testGetViewNameWithDefaultConfiguration(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + EXTENSION);
assertViewName(request, VIEW_NAME);
}
@Test
public void testGetViewNameWithCustomSeparator() {
request.setRequestURI(CONTEXT_PATH + VIEW_NAME + "/fiona" + EXTENSION);
@PathPatternsParameterizedTest
void testGetViewNameWithCustomSeparator(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + "/fiona" + EXTENSION);
this.translator.setSeparator("_");
assertViewName(VIEW_NAME + "_fiona");
assertViewName(request, VIEW_NAME + "_fiona");
}
@Test
public void testGetViewNameWithNoExtension() {
request.setRequestURI(CONTEXT_PATH + VIEW_NAME);
assertViewName(VIEW_NAME);
@PathPatternsParameterizedTest
void testGetViewNameWithNoExtension(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME);
assertViewName(request, VIEW_NAME);
}
@Test
public void testGetViewNameWithSemicolonContent() {
request.setRequestURI(CONTEXT_PATH + VIEW_NAME + ";a=A;b=B");
assertViewName(VIEW_NAME);
@PathPatternsParameterizedTest
void testGetViewNameWithSemicolonContent(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME + ";a=A;b=B");
assertViewName(request, VIEW_NAME);
}
@Test
public void testGetViewNameWithPrefix() {
@PathPatternsParameterizedTest
void testGetViewNameWithPrefix(Function<String, MockHttpServletRequest> requestFactory) {
final String prefix = "fiona_";
request.setRequestURI(CONTEXT_PATH + VIEW_NAME);
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME);
this.translator.setPrefix(prefix);
assertViewName(prefix + VIEW_NAME);
assertViewName(request, prefix + VIEW_NAME);
}
@Test
public void testGetViewNameWithNullPrefix() {
request.setRequestURI(CONTEXT_PATH + VIEW_NAME);
@PathPatternsParameterizedTest
void testGetViewNameWithNullPrefix(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME);
this.translator.setPrefix(null);
assertViewName(VIEW_NAME);
assertViewName(request, VIEW_NAME);
}
@Test
public void testGetViewNameWithSuffix() {
@PathPatternsParameterizedTest
void testGetViewNameWithSuffix(Function<String, MockHttpServletRequest> requestFactory) {
final String suffix = ".fiona";
request.setRequestURI(CONTEXT_PATH + VIEW_NAME);
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME);
this.translator.setSuffix(suffix);
assertViewName(VIEW_NAME + suffix);
assertViewName(request, VIEW_NAME + suffix);
}
@Test
public void testGetViewNameWithNullSuffix() {
request.setRequestURI(CONTEXT_PATH + VIEW_NAME);
@PathPatternsParameterizedTest
void testGetViewNameWithNullSuffix(Function<String, MockHttpServletRequest> requestFactory) {
MockHttpServletRequest request = requestFactory.apply(VIEW_NAME);
this.translator.setSuffix(null);
assertViewName(VIEW_NAME);
}
@Test
public void testTrySetUrlPathHelperToNull() {
try {
this.translator.setUrlPathHelper(null);
}
catch (IllegalArgumentException expected) {
}
assertViewName(request, VIEW_NAME);
}
private void assertViewName(String expectedViewName) {
String actualViewName = this.translator.getViewName(this.request);
private void assertViewName(MockHttpServletRequest request, String expectedViewName) {
String actualViewName = this.translator.getViewName(request);
assertThat(actualViewName).isNotNull();
assertThat(actualViewName).as("Did not get the expected viewName from the DefaultRequestToViewNameTranslator.getViewName(..)").isEqualTo(expectedViewName);
assertThat(actualViewName)
.as("Did not get the expected viewName from the DefaultRequestToViewNameTranslator.getViewName(..)")
.isEqualTo(expectedViewName);
}
}