Polish "simple" handler support

- correct name of HttpHandlerHandlerAdapter to WebHandlerHandlerAdapter
- shorten SimpleHandlerResultHandler to SimpleResultHandler
- add HandlerResult constructor without Model
- update tests
This commit is contained in:
Rossen Stoyanchev 2016-04-14 14:50:59 -04:00
parent ff6b639cf9
commit 3460e577ad
6 changed files with 65 additions and 44 deletions

View File

@ -44,6 +44,16 @@ public class HandlerResult {
private Function<Throwable, Mono<HandlerResult>> exceptionHandler;
/**
* Create a new {@code HandlerResult}.
* @param handler the handler that handled the request
* @param returnValue the return value from the handler possibly {@code null}
* @param returnValueType the return value type
*/
public HandlerResult(Object handler, Object returnValue, ResolvableType returnValueType) {
this(handler, returnValue, returnValueType, null);
}
/**
* Create a new {@code HandlerResult}.
* @param handler the handler that handled the request
@ -54,11 +64,10 @@ public class HandlerResult {
public HandlerResult(Object handler, Object returnValue, ResolvableType returnValueType, ModelMap model) {
Assert.notNull(handler, "'handler' is required");
Assert.notNull(returnValueType, "'returnValueType' is required");
Assert.notNull(model, "'model' is required");
this.handler = handler;
this.returnValue = Optional.ofNullable(returnValue);
this.returnValueType = returnValueType;
this.model = model;
this.model = (model != null ? model : new ExtendedModelMap());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@ -29,24 +29,28 @@ import org.springframework.web.reactive.HandlerResult;
import org.springframework.web.reactive.HandlerResultHandler;
import org.springframework.web.server.ServerWebExchange;
/**
* Supports {@link HandlerResult} with a {@code void} or {@code Publisher<Void>} value.
* An optional {link ConversionService} can be used to support types that can be converted to
* {@code Publisher<Void>}, like {@code Observable<Void>} or {@code CompletableFuture<Void>}.
* 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 SimpleHandlerResultHandler implements Ordered, HandlerResultHandler {
public class SimpleResultHandler implements Ordered, HandlerResultHandler {
private int order = Ordered.LOWEST_PRECEDENCE;
private ConversionService conversionService;
public SimpleHandlerResultHandler() {
public SimpleResultHandler() {
}
public SimpleHandlerResultHandler(ConversionService conversionService) {
public SimpleResultHandler(ConversionService conversionService) {
Assert.notNull(conversionService, "'conversionService' is required.");
this.conversionService = conversionService;
}
@ -61,30 +65,36 @@ public class SimpleHandlerResultHandler implements Ordered, HandlerResultHandler
return this.order;
}
@Override
public boolean supports(HandlerResult result) {
ResolvableType type = result.getReturnValueType();
return (type != null && Void.TYPE.equals(type.getRawClass()) ||
(isConvertibleToPublisher(type) && Void.class.isAssignableFrom(type.getGeneric(0).getRawClass())));
return (type != null && (Void.TYPE.equals(type.getRawClass()) || isConvertibleToVoidPublisher(type)));
}
private boolean isConvertibleToVoidPublisher(ResolvableType type) {
return (isConvertibleToPublisher(type) &&
Void.class.isAssignableFrom(type.getGeneric(0).getRawClass()));
}
private boolean isConvertibleToPublisher(ResolvableType type) {
return Publisher.class.isAssignableFrom(type.getRawClass()) ||
((this.conversionService != null) &&
this.conversionService.canConvert(type.getRawClass(), Publisher.class));
Class<?> clazz = type.getRawClass();
return (Publisher.class.isAssignableFrom(clazz) ||
((this.conversionService != null) && this.conversionService.canConvert(clazz, Publisher.class)));
}
@SuppressWarnings("unchecked")
@Override
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
Optional<Object> value = result.getReturnValue();
if (!value.isPresent() || Void.TYPE.equals(result.getReturnValueType().getRawClass())) {
Optional<Object> optional = result.getReturnValue();
if (!optional.isPresent()) {
return Mono.empty();
}
if (value.get() instanceof Mono) {
return (Mono<Void>) value.get();
Object returnValue = optional.get();
if (returnValue instanceof Mono) {
return (Mono<Void>) returnValue;
}
return Mono.from(this.conversionService.convert(value.get(), Publisher.class));
return Mono.from(this.conversionService.convert(returnValue, Publisher.class));
}
}

View File

@ -29,13 +29,12 @@ import org.springframework.web.server.WebHandler;
import org.springframework.web.server.ServerWebExchange;
/**
* Support use of {@link org.springframework.web.server.WebHandler} through the
* {@link DispatcherHandler}.
* Adapter to use a {@link WebHandler} through the {@link DispatcherHandler}.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
*/
public class HttpHandlerHandlerAdapter implements HandlerAdapter {
public class WebHandlerHandlerAdapter implements HandlerAdapter {
private static final ResolvableType PUBLISHER_VOID = ResolvableType.forClassWithGenerics(
Publisher.class, Void.class);
@ -49,9 +48,8 @@ public class HttpHandlerHandlerAdapter implements HandlerAdapter {
@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
WebHandler webHandler = (WebHandler) handler;
Mono<Void> completion = webHandler.handle(exchange);
ModelMap model = new ExtendedModelMap();
return Mono.just(new HandlerResult(webHandler, completion, PUBLISHER_VOID, model));
Mono<Void> mono = webHandler.handle(exchange);
return Mono.just(new HandlerResult(webHandler, mono, PUBLISHER_VOID));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* 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.
@ -27,7 +27,6 @@ import org.springframework.core.ResolvableType;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.convert.support.ReactiveStreamsToCompletableFutureConverter;
import org.springframework.core.convert.support.ReactiveStreamsToRxJava1Converter;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.HandlerResult;
@ -37,12 +36,12 @@ import static org.junit.Assert.assertTrue;
/**
* @author Sebastien Deleuze
*/
public class SimpleHandlerResultHandlerTests {
public class SimpleResultHandlerTests {
@Test
public void supports() throws NoSuchMethodException {
SimpleHandlerResultHandler resultHandler = new SimpleHandlerResultHandler();
SimpleResultHandler resultHandler = new SimpleResultHandler();
TestController controller = new TestController();
HandlerMethod hm = new HandlerMethod(controller, TestController.class.getMethod("voidReturnValue"));
@ -77,7 +76,7 @@ public class SimpleHandlerResultHandlerTests {
GenericConversionService conversionService = new GenericConversionService();
conversionService.addConverter(new ReactiveStreamsToCompletableFutureConverter());
conversionService.addConverter(new ReactiveStreamsToRxJava1Converter());
SimpleHandlerResultHandler resultHandler = new SimpleHandlerResultHandler(conversionService);
SimpleResultHandler resultHandler = new SimpleResultHandler(conversionService);
TestController controller = new TestController();
HandlerMethod hm = new HandlerMethod(controller, TestController.class.getMethod("voidReturnValue"));
@ -106,7 +105,7 @@ public class SimpleHandlerResultHandlerTests {
}
private HandlerResult createHandlerResult(HandlerMethod hm, ResolvableType type) {
return new HandlerResult(hm, null, type, new ExtendedModelMap());
return new HandlerResult(hm, null, type);
}

View File

@ -47,9 +47,11 @@ import static org.junit.Assert.assertEquals;
/**
* Integration tests with simple WebHandler's processing requests.
*
* @author Rossen Stoyanchev
*/
public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandlerIntegrationTests {
public class WebHandlerIntegrationTests extends AbstractHttpHandlerIntegrationTests {
private static final Charset UTF_8 = Charset.forName("UTF-8");
@ -59,14 +61,14 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler
StaticApplicationContext wac = new StaticApplicationContext();
wac.registerSingleton("hm", TestHandlerMapping.class);
wac.registerSingleton("ha", HttpHandlerHandlerAdapter.class);
wac.registerSingleton("rh", SimpleHandlerResultHandler.class);
wac.registerSingleton("ha", WebHandlerHandlerAdapter.class);
wac.registerSingleton("rh", SimpleResultHandler.class);
wac.refresh();
DispatcherHandler webHandler = new DispatcherHandler();
webHandler.setApplicationContext(wac);
DispatcherHandler dispatcherHandler = new DispatcherHandler();
dispatcherHandler.setApplicationContext(wac);
return WebHttpHandlerBuilder.webHandler(webHandler)
return WebHttpHandlerBuilder.webHandler(dispatcherHandler)
.exceptionHandlers(new ResponseStatusExceptionHandler())
.build();
}
@ -137,12 +139,16 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler
}
}
private static DataBuffer asDataBuffer(String text) {
return new DefaultDataBufferAllocator().allocateBuffer().write(text.getBytes(StandardCharsets.UTF_8));
}
private static class FooHandler implements WebHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
DataBuffer buffer = new DefaultDataBufferAllocator().allocateBuffer()
.write("foo".getBytes(StandardCharsets.UTF_8));
DataBuffer buffer = asDataBuffer("foo");
return exchange.getResponse().setBody(Flux.just(buffer));
}
}
@ -151,8 +157,7 @@ public class SimpleUrlHandlerMappingIntegrationTests extends AbstractHttpHandler
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
DataBuffer buffer = new DefaultDataBufferAllocator().allocateBuffer()
.write("bar".getBytes(StandardCharsets.UTF_8));
DataBuffer buffer = asDataBuffer("bar");
return exchange.getResponse().setBody(Flux.just(buffer));
}
}

View File

@ -67,7 +67,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.reactive.ViewResolver;
import org.springframework.web.reactive.handler.SimpleHandlerResultHandler;
import org.springframework.web.reactive.handler.SimpleResultHandler;
import org.springframework.web.reactive.view.ViewResolverResultHandler;
import org.springframework.web.reactive.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.reactive.view.freemarker.FreeMarkerViewResolver;
@ -421,8 +421,8 @@ public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrati
}
@Bean
public SimpleHandlerResultHandler simpleHandlerResultHandler() {
SimpleHandlerResultHandler resultHandler = new SimpleHandlerResultHandler(conversionService());
public SimpleResultHandler simpleHandlerResultHandler() {
SimpleResultHandler resultHandler = new SimpleResultHandler(conversionService());
resultHandler.setOrder(2);
return resultHandler;
}