Use method signature to refine RSocket @MessageMapping
Before this change an @MessageMapping could be matched to any RSocket interaction type, which is arguably too flexible, makes it difficult to reason what would happen in case of a significant mismatch of cardinality, e.g. request for Fire-And-Forget (1-to-0) mapped to a method that returns Flux, and could result in payloads being ignored, or not seen unintentionally. This commit checks @ConnectMapping method on startup and rejects them if they return any values (sync or async). It also refines each @MessageMapping to match only the RSocket interaction type it fits based on the input and output cardinality of the handler method. Subsequently if a request is not matched, we'll do a second search to identify partial matches (by route only) and raise a helpful error that explains which interaction type is actually supported. The reference docs has been updated to explain the options. Closes gh-23999
This commit is contained in:
@@ -586,7 +586,7 @@ indicates only that the message was successfully sent, and not that it was handl
|
||||
== Annotated Responders
|
||||
|
||||
RSocket responders can be implemented as `@MessageMapping` and `@ConnectMapping` methods.
|
||||
`@MessageMapping` methods handle individual requests, and `@ConnectMapping` methods handle
|
||||
`@MessageMapping` methods handle individual requests while `@ConnectMapping` methods handle
|
||||
connection-level events (setup and metadata push). Annotated responders are supported
|
||||
symmetrically, for responding from the server side and for responding from the client side.
|
||||
|
||||
@@ -760,20 +760,90 @@ class RadarsController {
|
||||
}
|
||||
----
|
||||
|
||||
You don't need to explicit specify the RSocket interaction type. Simply declare the
|
||||
expected input and output, and a route pattern. The supporting infrastructure will adapt
|
||||
matching requests.
|
||||
The above `@MessageMapping` method responds to a Request-Stream interaction having the
|
||||
route "locate.radars.within". It supports a flexible method signature with the option to
|
||||
use the following method arguments:
|
||||
|
||||
The following additional arguments are supported for `@MessageMapping` methods:
|
||||
[cols="1,3",options="header"]
|
||||
|===
|
||||
| Method Argument
|
||||
| Description
|
||||
|
||||
* `RSocketRequester` -- the requester for the connection associated with the request,
|
||||
to make requests to the remote end.
|
||||
* `@DestinationVariable` -- the value for a variable from the pattern, e.g.
|
||||
| `@Payload`
|
||||
| The payload of the request. This can be a concrete value of asynchronous types like
|
||||
`Mono` or `Flux`.
|
||||
|
||||
*Note:* Use of the annotation is optional. A method argument that is not a simple type
|
||||
and is not any of the other supported arguments, is assumed to be the expected payload.
|
||||
|
||||
| `RSocketRequester`
|
||||
| Requester for making requests to the remote end.
|
||||
|
||||
| `@DestinationVariable`
|
||||
| Value extracted from the route based on variables in the mapping pattern, e.g.
|
||||
`@MessageMapping("find.radar.{id}")`.
|
||||
* `@Header` -- access to a metadata value registered for extraction, as described in
|
||||
<<rsocket-metadata-extractor>>.
|
||||
* `@Headers Map<String, Object>` -- access to all metadata values registered for
|
||||
extraction, as described in <<rsocket-metadata-extractor>>.
|
||||
|
||||
| `@Header`
|
||||
| Metadata value registered for extraction as described in <<rsocket-metadata-extractor>>.
|
||||
|
||||
| `@Headers Map<String, Object>`
|
||||
| All metadata values registered for extraction as described in <<rsocket-metadata-extractor>>.
|
||||
|
||||
|===
|
||||
|
||||
The return value is expected to be one or more Objects to be serialized as response
|
||||
payloads. That can be asynchronous types like `Mono` or `Flux`, a concrete value, or
|
||||
either `void` or a no-value asynchronous type such as `Mono<Void>`.
|
||||
|
||||
The RSocket interaction type that an `@MessageMapping` method supports is determined from
|
||||
the cardinality of the input (i.e. `@Payload` argument) and of the output, where
|
||||
cardinality means the following:
|
||||
|
||||
[%autowidth]
|
||||
[cols=2*,options="header"]
|
||||
|===
|
||||
| Cardinality
|
||||
| Description
|
||||
|
||||
| 1
|
||||
| Either an explicit value, or a single-value asynchronous type such as `Mono<T>`.
|
||||
|
||||
| Many
|
||||
| A multi-value asynchronous type such as `Flux<T>`.
|
||||
|
||||
| 0
|
||||
| For input this means the method does not have an `@Payload` argument.
|
||||
|
||||
For output this is `void` or a no-value asynchronous type such as `Mono<Void>`.
|
||||
|===
|
||||
|
||||
The table below shows all input and output cardinality combinations and the corresponding
|
||||
interaction type(s):
|
||||
|
||||
[%autowidth]
|
||||
[cols=3*,options="header"]
|
||||
|===
|
||||
| Input Cardinality
|
||||
| Output Cardinality
|
||||
| Interaction Types
|
||||
|
||||
| 0, 1
|
||||
| 0
|
||||
| Fire-and-Forget, Request-Response
|
||||
|
||||
| 0, 1
|
||||
| 1
|
||||
| Request-Response
|
||||
|
||||
| 0, 1
|
||||
| Many
|
||||
| Request-Stream
|
||||
|
||||
| Many
|
||||
| 0, 1, Many
|
||||
| Request-Channel
|
||||
|
||||
|===
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user