/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.reactive.result; import java.util.Optional; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.core.Ordered; import org.springframework.core.ResolvableType; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.util.Assert; import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResultHandler; import org.springframework.web.server.ServerWebExchange; /** * A simple handler for return values of type {@code void}, or * {@code Publisher<Void>}, or if a {link ConversionService} is provided, also * of any other async return value types that can be converted to * {@code Publisher<Void>} such as {@code Observable<Void>} or * {@code CompletableFuture<Void>}. * * @author Sebastien Deleuze * @author Rossen Stoyanchev */ public class SimpleResultHandler implements Ordered, HandlerResultHandler { protected static final TypeDescriptor MONO_TYPE = TypeDescriptor.valueOf(Mono.class); protected static final TypeDescriptor FLUX_TYPE = TypeDescriptor.valueOf(Flux.class); private ConversionService conversionService; private int order = Ordered.LOWEST_PRECEDENCE; public SimpleResultHandler(ConversionService conversionService) { Assert.notNull(conversionService, "'conversionService' is required."); this.conversionService = conversionService; } /** * Return the configured {@link ConversionService}. */ public ConversionService getConversionService() { return this.conversionService; } /** * Set the order for this result handler relative to others. * <p>By default this is set to {@link Ordered#LOWEST_PRECEDENCE} and is * generally safe to use late in the order since it looks specifically for * {@code void} or async return types parameterized by {@code void}. * @param order the order */ public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } @Override public boolean supports(HandlerResult result) { ResolvableType type = result.getReturnType(); if (Void.TYPE.equals(type.getRawClass())) { return true; } TypeDescriptor source = new TypeDescriptor(result.getReturnTypeSource()); if (Publisher.class.isAssignableFrom(type.getRawClass()) || canConvert(source, MONO_TYPE) || canConvert(source, FLUX_TYPE)) { Class<?> clazz = result.getReturnType().getGeneric(0).getRawClass(); return Void.class.equals(clazz); } return false; } private boolean canConvert(TypeDescriptor source, TypeDescriptor target) { return getConversionService().canConvert(source, target); } @SuppressWarnings("unchecked") @Override public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) { Optional<Object> optional = result.getReturnValue(); if (!optional.isPresent()) { return Mono.empty(); } Object value = optional.get(); if (Publisher.class.isAssignableFrom(result.getReturnType().getRawClass())) { return Mono.from((Publisher<?>) value).then(); } TypeDescriptor source = new TypeDescriptor(result.getReturnTypeSource()); return canConvert(source, MONO_TYPE) ? ((Mono<Void>) getConversionService().convert(value, source, MONO_TYPE)) : ((Flux<Void>) getConversionService().convert(value, source, FLUX_TYPE)).single(); } }