spring HandlerMethodMappingTests 源码

  • 2022-08-08
  • 浏览 (312)

spring HandlerMethodMappingTests 代码

文件路径:/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java

/*
 * Copyright 2002-2021 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.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import org.springframework.web.util.UrlPathHelper;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;

/**
 * Test for {@link AbstractHandlerMethodMapping}.
 *
 * @author Arjen Poutsma
 * @author Rossen Stoyanchev
 */
@SuppressWarnings("unused")
public class HandlerMethodMappingTests {

	private MyHandlerMethodMapping mapping;

	private MyHandler handler;

	private Method method1;

	private Method method2;


	@BeforeEach
	public void setUp() throws Exception {
		this.mapping = new MyHandlerMethodMapping();
		this.handler = new MyHandler();
		this.method1 = handler.getClass().getMethod("handlerMethod1");
		this.method2 = handler.getClass().getMethod("handlerMethod2");
	}


	@Test
	public void registerDuplicates() {
		this.mapping.registerMapping("foo", this.handler, this.method1);
		assertThatIllegalStateException().isThrownBy(() ->
				this.mapping.registerMapping("foo", this.handler, this.method2));
	}

	@Test
	public void directMatch() throws Exception {
		this.mapping.registerMapping("/foo", this.handler, this.method1);
		this.mapping.registerMapping("/fo*", this.handler, this.method2);

		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
		HandlerMethod result = this.mapping.getHandlerInternal(request);

		assertThat(result.getMethod()).isEqualTo(method1);
		assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE)).isEqualTo(result);
		assertThat(this.mapping.getMatches()).containsExactly("/foo");
	}

	@Test
	public void patternMatch() throws Exception {
		this.mapping.registerMapping("/fo*", this.handler, this.method1);
		this.mapping.registerMapping("/f*", this.handler, this.method2);

		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
		HandlerMethod result = this.mapping.getHandlerInternal(request);
		assertThat(result.getMethod()).isEqualTo(method1);
		assertThat(request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE)).isEqualTo(result);
	}

	@Test
	public void ambiguousMatch() {
		this.mapping.registerMapping("/f?o", this.handler, this.method1);
		this.mapping.registerMapping("/fo?", this.handler, this.method2);

		assertThatIllegalStateException().isThrownBy(() ->
				this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo")));
	}

	@Test // gh-26490
	public void ambiguousMatchOnPreFlightRequestWithoutCorsConfig() throws Exception {
		this.mapping.registerMapping("/foo", this.handler, this.method1);
		this.mapping.registerMapping("/f??", this.handler, this.method2);

		MockHttpServletRequest request = new MockHttpServletRequest("OPTIONS", "/foo");
		request.addHeader(HttpHeaders.ORIGIN, "https://domain.com");
		request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");

		MockHttpServletResponse response = new MockHttpServletResponse();

		HandlerExecutionChain chain = this.mapping.getHandler(request);
		assertThat(chain).isNotNull();
		assertThat(chain.getHandler()).isInstanceOf(HttpRequestHandler.class);
		new HttpRequestHandlerAdapter().handle(request, response, chain.getHandler());

		assertThat(response.getStatus()).isEqualTo(403);
	}

	@Test // gh-26490
	public void ambiguousMatchOnPreFlightRequestWithCorsConfig() throws Exception {
		this.mapping.registerMapping("/f?o", this.handler, this.method1);
		this.mapping.registerMapping("/fo?", this.handler, this.handler.getClass().getMethod("corsHandlerMethod"));

		MockHttpServletRequest request = new MockHttpServletRequest("OPTIONS", "/foo");
		request.addHeader(HttpHeaders.ORIGIN, "https://domain.com");
		request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");

		MockHttpServletResponse response = new MockHttpServletResponse();

		HandlerExecutionChain chain = this.mapping.getHandler(request);
		assertThat(chain).isNotNull();
		assertThat(chain.getHandler()).isInstanceOf(HttpRequestHandler.class);
		new HttpRequestHandlerAdapter().handle(request, response, chain.getHandler());

		assertThat(response.getStatus()).isEqualTo(200);
		assertThat(response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain.com");
		assertThat(response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)).isEqualTo("GET");
	}

	@Test
	public void detectHandlerMethodsInAncestorContexts() {
		StaticApplicationContext cxt = new StaticApplicationContext();
		cxt.registerSingleton("myHandler", MyHandler.class);

		AbstractHandlerMethodMapping<String> mapping1 = new MyHandlerMethodMapping();
		mapping1.setApplicationContext(new StaticApplicationContext(cxt));
		mapping1.afterPropertiesSet();

		assertThat(mapping1.getHandlerMethods().size()).isEqualTo(0);

		AbstractHandlerMethodMapping<String> mapping2 = new MyHandlerMethodMapping();
		mapping2.setDetectHandlerMethodsInAncestorContexts(true);
		mapping2.setApplicationContext(new StaticApplicationContext(cxt));
		mapping2.afterPropertiesSet();

		assertThat(mapping2.getHandlerMethods().size()).isEqualTo(2);
	}

	@Test
	public void registerMapping() {
		String key1 = "/foo";
		String key2 = "/foo*";
		this.mapping.registerMapping(key1, this.handler, this.method1);
		this.mapping.registerMapping(key2, this.handler, this.method2);

		// Direct URL lookup

		List<String> directUrlMatches = this.mapping.getMappingRegistry().getMappingsByDirectPath(key1);
		assertThat(directUrlMatches).isNotNull();
		assertThat(directUrlMatches.size()).isEqualTo(1);
		assertThat(directUrlMatches.get(0)).isEqualTo(key1);

		// Mapping name lookup

		HandlerMethod handlerMethod1 = new HandlerMethod(this.handler, this.method1);
		HandlerMethod handlerMethod2 = new HandlerMethod(this.handler, this.method2);

		String name1 = this.method1.getName();
		List<HandlerMethod> handlerMethods = this.mapping.getMappingRegistry().getHandlerMethodsByMappingName(name1);
		assertThat(handlerMethods).isNotNull();
		assertThat(handlerMethods.size()).isEqualTo(1);
		assertThat(handlerMethods.get(0)).isEqualTo(handlerMethod1);

		String name2 = this.method2.getName();
		handlerMethods = this.mapping.getMappingRegistry().getHandlerMethodsByMappingName(name2);
		assertThat(handlerMethods).isNotNull();
		assertThat(handlerMethods.size()).isEqualTo(1);
		assertThat(handlerMethods.get(0)).isEqualTo(handlerMethod2);
	}

	@Test
	public void registerMappingWithSameMethodAndTwoHandlerInstances() {
		String key1 = "foo";
		String key2 = "bar";

		MyHandler handler1 = new MyHandler();
		MyHandler handler2 = new MyHandler();

		HandlerMethod handlerMethod1 = new HandlerMethod(handler1, this.method1);
		HandlerMethod handlerMethod2 = new HandlerMethod(handler2, this.method1);

		this.mapping.registerMapping(key1, handler1, this.method1);
		this.mapping.registerMapping(key2, handler2, this.method1);

		// Direct URL lookup

		List<String> directUrlMatches = this.mapping.getMappingRegistry().getMappingsByDirectPath(key1);
		assertThat(directUrlMatches).isNotNull();
		assertThat(directUrlMatches.size()).isEqualTo(1);
		assertThat(directUrlMatches.get(0)).isEqualTo(key1);

		// Mapping name lookup

		String name = this.method1.getName();
		List<HandlerMethod> handlerMethods = this.mapping.getMappingRegistry().getHandlerMethodsByMappingName(name);
		assertThat(handlerMethods).isNotNull();
		assertThat(handlerMethods.size()).isEqualTo(2);
		assertThat(handlerMethods.get(0)).isEqualTo(handlerMethod1);
		assertThat(handlerMethods.get(1)).isEqualTo(handlerMethod2);
	}

	@Test
	public void unregisterMapping() throws Exception {
		String key = "foo";
		HandlerMethod handlerMethod = new HandlerMethod(this.handler, this.method1);

		this.mapping.registerMapping(key, this.handler, this.method1);
		assertThat(this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", key))).isNotNull();

		this.mapping.unregisterMapping(key);
		assertThat(mapping.getHandlerInternal(new MockHttpServletRequest("GET", key))).isNull();
		assertThat(this.mapping.getMappingRegistry().getMappingsByDirectPath(key)).isNull();
		assertThat(this.mapping.getMappingRegistry().getHandlerMethodsByMappingName(this.method1.getName())).isNull();
		assertThat(this.mapping.getMappingRegistry().getCorsConfiguration(handlerMethod)).isNull();
	}

	@Test
	public void getCorsConfigWithBeanNameHandler() throws Exception {
		String key = "foo";
		String beanName = "handler1";

		StaticWebApplicationContext context = new StaticWebApplicationContext();
		context.registerSingleton(beanName, MyHandler.class);

		this.mapping.setApplicationContext(context);
		this.mapping.registerMapping(key, beanName, this.method1);
		HandlerMethod handlerMethod = this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", key));
	}



	private static class MyHandlerMethodMapping extends AbstractHandlerMethodMapping<String> {

		private UrlPathHelper pathHelper = new UrlPathHelper();

		private PathMatcher pathMatcher = new AntPathMatcher();

		private final List<String> matches = new ArrayList<>();

		public MyHandlerMethodMapping() {
			setHandlerMethodMappingNamingStrategy(new SimpleMappingNamingStrategy());
		}

		public List<String> getMatches() {
			return this.matches;
		}

		@Override
		protected boolean isHandler(Class<?> beanType) {
			return true;
		}

		@Override
		protected Set<String> getDirectPaths(String mapping) {
			return (pathMatcher.isPattern(mapping) ? Collections.emptySet() : Collections.singleton(mapping));
		}

		@Override
		protected String getMappingForMethod(Method method, Class<?> handlerType) {
			String methodName = method.getName();
			return methodName.startsWith("handler") ? methodName : null;
		}

		@Override
		protected CorsConfiguration initCorsConfiguration(Object handler, Method method, String mapping) {
			CrossOrigin crossOrigin = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
			if (crossOrigin != null) {
				CorsConfiguration corsConfig = new CorsConfiguration();
				corsConfig.setAllowedOrigins(Collections.singletonList("https://domain.com"));
				return corsConfig;
			}
			return null;
		}

		@Override
		protected String getMatchingMapping(String pattern, HttpServletRequest request) {
			String lookupPath = this.pathHelper.getLookupPathForRequest(request);
			String match = (this.pathMatcher.match(pattern, lookupPath) ? pattern : null);
			if (match != null) {
				this.matches.add(match);
			}
			return match;
		}

		@Override
		protected Comparator<String> getMappingComparator(HttpServletRequest request) {
			String lookupPath = this.pathHelper.getLookupPathForRequest(request);
			return this.pathMatcher.getPatternComparator(lookupPath);
		}

	}

	private static class SimpleMappingNamingStrategy implements HandlerMethodMappingNamingStrategy<String> {

		@Override
		public String getName(HandlerMethod handlerMethod, String mapping) {
			return handlerMethod.getMethod().getName();
		}
	}

	@Controller
	static class MyHandler {

		@RequestMapping
		public void handlerMethod1() {
		}

		@RequestMapping
		public void handlerMethod2() {
		}

		@RequestMapping
		@CrossOrigin(originPatterns = "*")
		public void corsHandlerMethod() {
		}
	}
}

相关信息

spring 源码目录

相关文章

spring BeanNameUrlHandlerMappingTests 源码

spring CorsAbstractHandlerMappingTests 源码

spring HandlerMappingIntrospectorTests 源码

spring HandlerMappingTests 源码

spring MappedInterceptorTests 源码

spring PathMatchingUrlHandlerMappingTests 源码

spring PathPatternsParameterizedTest 源码

spring PathPatternsTestUtils 源码

spring SimpleMappingExceptionResolverTests 源码

spring SimpleUrlHandlerMappingTests 源码

0  赞