@Override public void afterPropertiesSet() throws Exception { Assert.notNull(this.applicationContext, "ApplicationContext is required"); if (CollectionUtils.isEmpty(this.messageReaders)) { ServerCodecConfigurer codecConfigurer = ServerCodecConfigurer.create(); this.messageReaders = codecConfigurer.getReaders(); } if (this.argumentResolverConfigurer == null) { this.argumentResolverConfigurer = new ArgumentResolverConfigurer(); } if (this.reactiveAdapterRegistry == null) { this.reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance(); } this.methodResolver = new ControllerMethodResolver(this.argumentResolverConfigurer, this.reactiveAdapterRegistry, this.applicationContext, this.messageReaders); this.modelInitializer = new ModelInitializer(this.methodResolver, this.reactiveAdapterRegistry); }
private Mono<HandlerResult> handleException(Throwable exception, HandlerMethod handlerMethod, BindingContext bindingContext, ServerWebExchange exchange) { Assert.state(this.methodResolver != null, "Not initialized"); // Success and error responses may use different content types exchange.getAttributes().remove(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod(exception, handlerMethod); if (invocable != null) { try { if (logger.isDebugEnabled()) { logger.debug(exchange.getLogPrefix() + "Using @ExceptionHandler " + invocable); } bindingContext.getModel().asMap().clear(); Throwable cause = exception.getCause(); if (cause != null) { return invocable.invoke(exchange, bindingContext, exception, cause, handlerMethod); } else { return invocable.invoke(exchange, bindingContext, exception, handlerMethod); } } catch (Throwable invocationEx) { if (logger.isWarnEnabled()) { logger.warn(exchange.getLogPrefix() + "Failure in @ExceptionHandler " + invocable, invocationEx); } } } return Mono.error(exception); }
/** * Find {@code @ModelAttribute} methods in {@code @ControllerAdvice} * components or in the controller of the given {@code @RequestMapping} method. */ public List<InvocableHandlerMethod> getModelAttributeMethods(HandlerMethod handlerMethod) { List<InvocableHandlerMethod> result = new ArrayList<>(); Class<?> handlerType = handlerMethod.getBeanType(); // Global methods first this.modelAttributeAdviceCache.forEach((adviceBean, methods) -> { if (adviceBean.isApplicableToBeanType(handlerType)) { Object bean = adviceBean.resolveBean(); methods.forEach(method -> result.add(createAttributeMethod(bean, method))); } }); this.modelAttributeMethodCache .computeIfAbsent(handlerType, clazz -> MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS)) .forEach(method -> { Object bean = handlerMethod.getBean(); result.add(createAttributeMethod(bean, method)); }); return result; }
ControllerMethodResolver(ArgumentResolverConfigurer customResolvers, ReactiveAdapterRegistry reactiveRegistry, ConfigurableApplicationContext context, List<HttpMessageReader<?>> readers) { Assert.notNull(customResolvers, "ArgumentResolverConfigurer is required"); Assert.notNull(readers, "'messageReaders' is required"); Assert.notNull(reactiveRegistry, "ReactiveAdapterRegistry is required"); Assert.notNull(context, "ApplicationContext is required"); this.initBinderResolvers = initBinderResolvers(customResolvers, reactiveRegistry, context); this.modelAttributeResolvers = modelMethodResolvers(customResolvers, reactiveRegistry, context); this.requestMappingResolvers = requestMappingResolvers(customResolvers, reactiveRegistry, context, readers); this.exceptionHandlerResolvers = exceptionHandlerResolvers(customResolvers, reactiveRegistry, context); this.reactiveAdapterRegistry = reactiveRegistry; initControllerAdviceCaches(context); }
@Override public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) { HandlerMethod handlerMethod = (HandlerMethod) handler; Assert.state(this.methodResolver != null && this.modelInitializer != null, "Not initialized"); InitBinderBindingContext bindingContext = new InitBinderBindingContext( getWebBindingInitializer(), this.methodResolver.getInitBinderMethods(handlerMethod)); InvocableHandlerMethod invocableMethod = this.methodResolver.getRequestMappingMethod(handlerMethod); Function<Throwable, Mono<HandlerResult>> exceptionHandler = ex -> handleException(ex, handlerMethod, bindingContext, exchange); return this.modelInitializer .initModel(handlerMethod, bindingContext, exchange) .then(Mono.defer(() -> invocableMethod.invoke(exchange, bindingContext))) .doOnNext(result -> result.setExceptionHandler(exceptionHandler)) .doOnNext(result -> bindingContext.saveModel()) .onErrorResume(exceptionHandler); }
this.methodResolver.getModelAttributeMethods(handlerMethod); this.methodResolver.getSessionAttributesHandler(handlerMethod);
@Test public void initBinderArgumentResolvers() { 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(MatrixVariableMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(MatrixVariableMapMethodArgumentResolver.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 modelAttributeArgumentResolvers() { 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(PathVariableMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PathVariableMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(MatrixVariableMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(MatrixVariableMapMethodArgumentResolver.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 requestMappingArgumentResolvers() { InvocableHandlerMethod invocable = this.methodResolver.getRequestMappingMethod(this.handlerMethod); List<HandlerMethodArgumentResolver> resolvers = invocable.getResolvers();
/** * Find {@code @InitBinder} methods in {@code @ControllerAdvice} components * or in the controller of the given {@code @RequestMapping} method. */ public List<SyncInvocableHandlerMethod> getInitBinderMethods(HandlerMethod handlerMethod) { List<SyncInvocableHandlerMethod> result = new ArrayList<>(); Class<?> handlerType = handlerMethod.getBeanType(); // Global methods first this.initBinderAdviceCache.forEach((adviceBean, methods) -> { if (adviceBean.isApplicableToBeanType(handlerType)) { Object bean = adviceBean.resolveBean(); methods.forEach(method -> result.add(getInitBinderMethod(bean, method))); } }); this.initBinderMethodCache .computeIfAbsent(handlerType, clazz -> MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS)) .forEach(method -> { Object bean = handlerMethod.getBean(); result.add(getInitBinderMethod(bean, method)); }); return result; }
@Test public void exceptionHandlerFromControllerAdvice() { InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod( new IllegalStateException("reason"), this.handlerMethod); assertNotNull(invocable); assertEquals(TestControllerAdvice.class, invocable.getBeanType()); }
@Before public void setUp() throws Exception { ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance(); ArgumentResolverConfigurer resolverConfigurer = new ArgumentResolverConfigurer(); resolverConfigurer.addCustomResolver(new ModelArgumentResolver(adapterRegistry)); ControllerMethodResolver methodResolver = new ControllerMethodResolver( resolverConfigurer, adapterRegistry, new StaticApplicationContext(), Collections.emptyList()); this.modelInitializer = new ModelInitializer(methodResolver, adapterRegistry); }
@Test public void exceptionHandlerArgumentResolvers() { InvocableHandlerMethod invocable = this.methodResolver.getExceptionHandlerMethod( new ResponseStatusException(HttpStatus.BAD_REQUEST, "reason"), this.handlerMethod); assertNotNull("No match", invocable); 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(PathVariableMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(PathVariableMapMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(MatrixVariableMethodArgumentResolver.class, next(resolvers, index).getClass()); assertEquals(MatrixVariableMapMethodArgumentResolver.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()); }
@Before public void setup() { 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, ReactiveAdapterRegistry.getSharedInstance(), applicationContext, codecs.getReaders()); Method method = ResolvableMethod.on(TestController.class).mockCall(TestController::handle).method(); this.handlerMethod = new HandlerMethod(new TestController(), method); }