HandlerAdapter's should always be able to provide a MethodParameter
which in turn ensures that HandlerResultHandler's have full type
information from method declarations.
This commit also introduces ResolvableMethod for use in tests to make
it easy to obtain MethodParameter return types.
Issue: #128
This commit adds a Decoder#decodeOne() method in order
to handle correctly the streaming versus one value
deserialization based on the type provided by the user.
For example, if a List parameter is provided in a controller
method, Jackson will be called once, while if the user provides
a Flux or an Observable parameter, Jackson will be called for
each element.
This commit replaces Reactive Streams converters for RxJava1 and
CompletableFuture with Reactor specific ones. The results in conversion
that preserves stream semantics, i.e. Mono vs Flux.
For example this is allowed:
Flux -> Observable
Mono -> Single
Mono -> CompletableFuture
This is not allowed:
Flux -> Single
Mono -> Observable
Flux -> CompletableFuture
As a result it is now possible to check through the ConversionService
if a target type to convert to is a stream of many or of one which is
useful for decoding purposes.
The commit also adds PublisherToFluxConverter to allow conversion from
raw Publisher to Flux. The reverse is not necessary since Flux is a
Publisher and it's a no-op conversion.
The DefaultWebSessionManager now uses Mono.defer to protect the call
to getSession from parsing session cookies immediately. This allows
pre-initializing the Mono<WebSession> upfront vs using a lock.
When in state DATA_AVAILABLE if there are simultaneous invocations of
AbstractRequestBodyPublisher.RequestBodySubscription.request and
ReadListener.onDataAvailable, the first one will process the available
data, the second one should not throw an exception because thus it will
signal to web container that there are problems while there are not.
Refactored Undertow support to register a response listener only when
the body is written to, as opposed to registering it at startup. The
reason for this is that getting the response channel from the
HttpServerExchange commits the status and response, making it impossible
to change them after the fact.
Fixed issue #119.
When there are simultaneous invocations of onWritePossible, only the
first one should succeed. This can happens when
AbstractResponseBodySubscriber.onNext and
WriteListener.onWritePossible() are called respectively by the
application and the web container.
Reactored Servlet 3.1 and Undertow request support
(AbstractResponseBodySubscriber) to use an internal state machine,
making thread-safity a lot easier.
Currently ResponseEntityResultHandler is ordered lower than
ResponseBodyResultHandler by default whch means a ResponseEntity
should not be picked by the ResponseBodyResultHandler.
However as it is easy to have both ResponseEntity and @ResponseBody
e.g. in @RestControler (or even by mistake) and in general it makes
sense for ResponseBodyResultHandler to explicitly recognize and
ignore the ResponseEntity return type.
Before this commit only ResponseEntity with async body was supported,
e.g. ResponseEntity<Mono<String>>
This commit also adds suppport for an asyn wrapper around,
e.g. Mono<ResponseEntity<String>.
Introduce separate test classes for each base class in the hierarchy
above @ResponseBody and ResponseEntity result handlers.
Also start porting existing unit test cases for @ResponseBody and
ResponseEntity return value handlers.
Reactored Servlet 3.1 and Undertow response support into an
AbstractResponseBodySubscriber that uses an internal state machine,
making thread-safity a lot easier.
Flux<SseEvent> is Spring Web Reactive equivalent to Spring MVC
SseEmitter type. It allows to send Server-Sent Events in a reactive way.
Sending Flux<String> or Flux<Pojo> is equivalent to sending
Flux<SseEvent> with the data property set to the String or
Pojo value. For example:
@RestController
public class SseController {
@RequestMapping("/sse/string")
Flux<String> string() {
return Flux.interval(Duration.ofSeconds(1)).map(l -> "foo " + l);
}
@RequestMapping("/sse/person")
Flux<Person> person() {
return Flux.interval(Duration.ofSeconds(1)).map(l -> new Person(Long.toString(l), "foo", "bar"));
}
@RequestMapping("/sse-raw")
Flux<SseEvent> sse() {
return Flux.interval(Duration.ofSeconds(1)).map(l -> {
SseEvent event = new SseEvent();
event.setId(Long.toString(l));
event.setData("foo\nbar");
event.setComment("bar\nbaz");
return event;
});
}
}
This commit also fixes an issue in the HTTP client that used the
wrapper type instead of the element type. As a consequence, due
to type erasure, we now have to specify the type of the content
in DefaultHttpRequestBuilder#contentStream().
Renamed getSupportedMimeTypes() to getEncodableMimeTypes and
getDecodableMimeTypes. This will allow for both Encoder and Decoder to
be implemented in the same class.
This issue fixes#113.
The Pojo test class from the codec package will end up in spring-core.
This commit ensures it is used only from classes that also belong to
spring-core.
After the fix for ReactiveX/RxNetty#509 we can remove the workaround
to concatenate with an empty Observable for the request body and
upgrade to the latest RxJava 1.1.5.
Issues: #103
ViewResolutionResultHandler and ResponseBodyResultHandler now share
a common base class ContentNegotiatingResultHandlerSupport that
supports content negotiation.
For view resolution we compare against the supported media types of
resolved View instances, which may include default View's delegating
to an HttpMessageConverter (e.g. JSON, XML, rendering).
View now returns Mono<Void> rather than Flux<DataBuffer> which aligns
more closely with the reactive HttpMessageConverter vs the Encoder.
The change was prompted by the upcoming implementation of a View that
delegates to an existing HttpMessageConverter e.g. for JSON, XML.
The resulting change also brings the reactive View closer in spirit to
the View from spring-webmvc which returns void.
The ContentNegotiatingResultHandlerSupport base class encapsulates the
logic for content negotiation needed for both @ResponseBody and view
resolution result handling.
This commit adds support for Model-related return values types such as
Map, Model, @ModelAttribute annotated, and non-simple types, which
helps to clarify the logic in ViewResolutionResultHandler.
In order to be more "reactive", changed StringDecoder's default from
merging all buffers in the stream to a single buffer into splitting the
buffers along newline (\r, \n) characters.
This commit introduces DataBuffer.indexOf(IntPredicate) and
lastIndexOf(IntPredicate), for finding the index of a byte in a
DataBuffer.
It also introduces DataBufferUtils.tokenize, which tokenizes a
DataBuffer into separate tokens, given a delimiter function.
While View and ViewResolver play the same role as in spring-webmvc they
are now abstracted behind the HandlerResultHandler abstraction so that
top-level contracts don't reference them and the DispatcherHandler is
also unaware of their existence.
Furthermore view resolution and response body handling which are now at
the same level of abstraction (each is a HandlerResultHandler) will
also share code for content negotiation, so it makes sense for them to
be side by side.
This commit moves the reactive.view package to reactive.result.view
with the View and ViewResolver contracts (previously in the top-level
reactive package) also moving there.