/* * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.reactive.result.method.annotation; import java.lang.reflect.Method; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Before; import org.junit.Test; import reactor.core.publisher.Mono; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.MethodParameter; import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.codec.ByteArrayDecoder; import org.springframework.core.codec.ByteBufferDecoder; import org.springframework.http.HttpStatus; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.ResolvableMethod; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; import org.springframework.web.reactive.result.method.InvocableHandlerMethod; import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver; import org.springframework.web.reactive.result.method.SyncInvocableHandlerMethod; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import static org.junit.Assert.*; /** * Unit tests for {@link ControllerMethodResolver}. * @author Rossen Stoyanchev */ public class ControllerMethodResolverTests { private ControllerMethodResolver methodResolver; private HandlerMethod handlerMethod; @Before public void setUp() throws Exception { ArgumentResolverConfigurer resolvers = new ArgumentResolverConfigurer(); resolvers.addCustomResolver(new CustomArgumentResolver()); resolvers.addCustomResolver(new CustomSyncArgumentResolver()); ServerCodecConfigurer codecs = ServerCodecConfigurer.create(); codecs.customCodecs().decoder(new ByteArrayDecoder()); codecs.customCodecs().decoder(new ByteBufferDecoder()); AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.registerBean(TestControllerAdvice.class); applicationContext.refresh(); this.methodResolver = new ControllerMethodResolver( resolvers, codecs, new ReactiveAdapterRegistry(), applicationContext); Method method = ResolvableMethod.on(TestController.class).mockCall(TestController::handle).method(); this.handlerMethod = new HandlerMethod(new TestController(), method); } @Test public void requestMappingArgumentResolvers() throws Exception { InvocableHandlerMethod invocable = this.methodResolver.getRequestMappingMethod(this.handlerMethod); List<HandlerMethodArgumentResolver> resolvers = invocable.getResolvers(); AtomicInteger index = new AtomicInteger(-1); assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestParamMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestPartMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PathVariableMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PathVariableMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestBodyArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ModelAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestHeaderMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestHeaderMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CookieValueMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ExpressionValueMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(SessionAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(HttpEntityArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ModelArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ErrorsMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ServerWebExchangeArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PrincipalArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(WebSessionArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CustomArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CustomSyncArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ModelAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); } @Test public void modelAttributeArgumentResolvers() throws Exception { List<InvocableHandlerMethod> methods = this.methodResolver.getModelAttributeMethods(this.handlerMethod); assertEquals("Expected one each from Controller + ControllerAdvice", 2, methods.size()); InvocableHandlerMethod invocable = methods.get(0); List<HandlerMethodArgumentResolver> resolvers = invocable.getResolvers(); AtomicInteger index = new AtomicInteger(-1); assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestParamMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestPartMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PathVariableMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PathVariableMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ModelAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestHeaderMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestHeaderMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CookieValueMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ExpressionValueMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(SessionAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ModelArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ErrorsMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ServerWebExchangeArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PrincipalArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(WebSessionArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CustomArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CustomSyncArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ModelAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); } @Test public void initBinderArgumentResolvers() throws Exception { List<SyncInvocableHandlerMethod> methods = this.methodResolver.getInitBinderMethods(this.handlerMethod); assertEquals("Expected one each from Controller + ControllerAdvice", 2, methods.size()); SyncInvocableHandlerMethod invocable = methods.get(0); List<SyncHandlerMethodArgumentResolver> resolvers = invocable.getResolvers(); AtomicInteger index = new AtomicInteger(-1); assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestParamMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PathVariableMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PathVariableMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestHeaderMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestHeaderMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CookieValueMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ExpressionValueMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ModelArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ServerWebExchangeArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CustomSyncArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass()); } @Test public void exceptionHandlerArgumentResolvers() throws Exception { Optional<InvocableHandlerMethod> optional = this.methodResolver.getExceptionHandlerMethod( new ResponseStatusException(HttpStatus.BAD_REQUEST, "reason"), this.handlerMethod); InvocableHandlerMethod invocable = optional.orElseThrow(() -> new AssertionError("No match")); assertEquals(TestController.class, invocable.getBeanType()); List<HandlerMethodArgumentResolver> resolvers = invocable.getResolvers(); AtomicInteger index = new AtomicInteger(-1); assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestParamMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestPartMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PathVariableMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PathVariableMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestHeaderMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestHeaderMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CookieValueMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ExpressionValueMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(SessionAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestAttributeMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ModelArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(ServerWebExchangeArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PrincipalArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(WebSessionArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CustomArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(CustomSyncArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(RequestParamMethodArgumentResolver.class, next(resolvers, index).getClass()); } @Test public void exceptionHandlerFromControllerAdvice() throws Exception { Optional<InvocableHandlerMethod> optional = this.methodResolver.getExceptionHandlerMethod( new IllegalStateException("reason"), this.handlerMethod); InvocableHandlerMethod invocable = optional.orElseThrow(() -> new AssertionError("No match")); assertNotNull(invocable); assertEquals(TestControllerAdvice.class, invocable.getBeanType()); } private static HandlerMethodArgumentResolver next( List<? extends HandlerMethodArgumentResolver> resolvers, AtomicInteger index) { return resolvers.get(index.incrementAndGet()); } @Controller private static class TestController { @InitBinder void initDataBinder() {} @ModelAttribute void initModel() {} @GetMapping void handle() {} @ExceptionHandler void handleException(ResponseStatusException ex) {} } @ControllerAdvice private static class TestControllerAdvice { @InitBinder void initDataBinder() {} @ModelAttribute void initModel() {} @ExceptionHandler void handleException(IllegalStateException ex) {} } private static class CustomArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter p) { return false; } @Override public Mono<Object> resolveArgument(MethodParameter p, BindingContext c, ServerWebExchange e) { return null; } } private static class CustomSyncArgumentResolver extends CustomArgumentResolver implements SyncHandlerMethodArgumentResolver { @Override public Optional<Object> resolveArgumentValue(MethodParameter p, BindingContext c, ServerWebExchange e) { return null; } } }