|
|
|
|
@ -1372,82 +1372,146 @@ kinds of arguments and return values supported.
@@ -1372,82 +1372,146 @@ kinds of arguments and return values supported.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[websocket-stomp-handle-annotations]] |
|
|
|
|
=== Handler methods |
|
|
|
|
=== Annotated Controllers |
|
|
|
|
|
|
|
|
|
The `@MessageMapping` annotation is supported on methods of `@Controller` classes. |
|
|
|
|
It can be used for mapping methods to message destinations and can also be combined |
|
|
|
|
with the type-level `@MessageMapping` for expressing shared mappings across all |
|
|
|
|
annotated methods within a controller. |
|
|
|
|
Applications can use annotated `@Controller` classes to handle messages from clients. |
|
|
|
|
Such classes can declare `@MessageMapping`, `@SubscribeMapping`, and `@ExceptionHandler` |
|
|
|
|
methods as described next. |
|
|
|
|
|
|
|
|
|
By default destination mappings are treated as Ant-style, slash-separated, path |
|
|
|
|
patterns, e.g. "/foo*", "/foo/**". etc. They can also contain template variables, |
|
|
|
|
e.g. "/foo/{id}" that can then be referenced via `@DestinationVariable`-annotated |
|
|
|
|
method arguments. |
|
|
|
|
|
|
|
|
|
[NOTE] |
|
|
|
|
[[websocket-stomp-message-mapping]] |
|
|
|
|
==== `@MessageMapping` |
|
|
|
|
|
|
|
|
|
The `@MessageMapping` annotation can be used on methods to route messages based on their |
|
|
|
|
destination. It is supported at the method level as well as at the type level. At type |
|
|
|
|
level `@MessageMapping` is used to express shared mappings across all methods in a |
|
|
|
|
controller. |
|
|
|
|
|
|
|
|
|
By default destination mappings are expected to be Ant-style, path patterns, e.g. "/foo*", |
|
|
|
|
"/foo/**". The patterns include support for template variables, e.g. "/foo/{id}", that can |
|
|
|
|
be referenced with `@DestinationVariable` method arguments. |
|
|
|
|
|
|
|
|
|
[TIP] |
|
|
|
|
==== |
|
|
|
|
Applications can also use dot-separated destinations (vs slash). |
|
|
|
|
Applications can choose to switch to a dot-separated destination convention. |
|
|
|
|
See <<websocket-stomp-destination-separator>>. |
|
|
|
|
==== |
|
|
|
|
|
|
|
|
|
The following method arguments are supported for `@MessageMapping` methods: |
|
|
|
|
|
|
|
|
|
* `Message` method argument to get access to the complete message being processed. |
|
|
|
|
* `@Payload`-annotated argument for access to the payload of a message, converted with |
|
|
|
|
a `org.springframework.messaging.converter.MessageConverter`. |
|
|
|
|
The presence of the annotation is not required since it is assumed by default. |
|
|
|
|
Payload method arguments annotated with validation annotations (like `@Validated`) will |
|
|
|
|
be subject to JSR-303 validation. |
|
|
|
|
* `@Header`-annotated arguments for access to a specific header value along with |
|
|
|
|
type conversion using an `org.springframework.core.convert.converter.Converter` |
|
|
|
|
if necessary. |
|
|
|
|
* `@Headers`-annotated method argument that must also be assignable to `java.util.Map` |
|
|
|
|
for access to all headers in the message. |
|
|
|
|
* `MessageHeaders` method argument for getting access to a map of all headers. |
|
|
|
|
* `MessageHeaderAccessor`, `SimpMessageHeaderAccessor`, or `StompHeaderAccessor` |
|
|
|
|
for access to headers via typed accessor methods. |
|
|
|
|
* `@DestinationVariable`-annotated arguments for access to template |
|
|
|
|
variables extracted from the message destination. Values will be converted to |
|
|
|
|
the declared method argument type as necessary. |
|
|
|
|
* `java.security.Principal` method arguments reflecting the user logged in at |
|
|
|
|
the time of the WebSocket HTTP handshake. |
|
|
|
|
|
|
|
|
|
A return value from an `@MessageMapping` method will be converted with a |
|
|
|
|
`org.springframework.messaging.converter.MessageConverter` and used as the body |
|
|
|
|
of a new message that is then sent, by default, to the `"brokerChannel"` with |
|
|
|
|
the same destination as the client message but using the prefix `"/topic"` by |
|
|
|
|
default. An `@SendTo` message level annotation can be used to specify any |
|
|
|
|
other destination instead. It can also be set a class-level to share a common |
|
|
|
|
`@MessageMapping` methods can have flexible signatures with the following arguments: |
|
|
|
|
|
|
|
|
|
[cols="1,2", options="header"] |
|
|
|
|
|=== |
|
|
|
|
| Method argument | Description |
|
|
|
|
|
|
|
|
|
| `Message` |
|
|
|
|
| For access to the complete message. |
|
|
|
|
|
|
|
|
|
| `MessageHeaders` |
|
|
|
|
| For access to the headers within the `Message`. |
|
|
|
|
|
|
|
|
|
| `MessageHeaderAccessor`, `SimpMessageHeaderAccessor`, `StompHeaderAccessor` |
|
|
|
|
| For access to the headers via typed accessor methods. |
|
|
|
|
|
|
|
|
|
| `@Payload` |
|
|
|
|
| For access to the payload of the message, converted (e.g. from JSON) via a configured |
|
|
|
|
`MessageConverter`. |
|
|
|
|
|
|
|
|
|
The presence of this annotation is not required since it is assumed by default if no |
|
|
|
|
other argument is matched. |
|
|
|
|
|
|
|
|
|
Payload arguments may be annotated with `@javax.validation.Valid` or Spring's `@Validated` |
|
|
|
|
in order to be automatically validated. |
|
|
|
|
|
|
|
|
|
| `@Header` |
|
|
|
|
| For access to a specific header value along with type conversion using an |
|
|
|
|
`org.springframework.core.convert.converter.Converter` if necessary. |
|
|
|
|
|
|
|
|
|
| `@Headers` |
|
|
|
|
| For access to all headers in the message. This argument must be assignable to |
|
|
|
|
`java.util.Map`. |
|
|
|
|
|
|
|
|
|
| `@DestinationVariable` |
|
|
|
|
| For access to template variables extracted from the message destination. |
|
|
|
|
Values will be converted to the declared method argument type as necessary. |
|
|
|
|
|
|
|
|
|
| `java.security.Principal` |
|
|
|
|
| Reflects the user logged in at the time of the WebSocket HTTP handshake. |
|
|
|
|
|
|
|
|
|
|=== |
|
|
|
|
|
|
|
|
|
When an `@MessageMapping` method returns a value, by default the value is serialized to |
|
|
|
|
a payload through a configured `MessageConverter`, and then sent as a `Message` to the |
|
|
|
|
`"brokerChannel"` from where it is broadcast to subscribers. The destination of the |
|
|
|
|
outbound message is the same as that of the inbound message but prefixed with `"/topic"`. |
|
|
|
|
|
|
|
|
|
You can use the `@SendTo` method annotation to customize the destination to send |
|
|
|
|
the payload to. `@SendTo` can also be used at the class level to share a default target |
|
|
|
|
destination to send messages to. `@SendToUser` is an variant for sending messages only to |
|
|
|
|
the user associated with a message. See <<websocket-stomp-user-destination>> for details. |
|
|
|
|
|
|
|
|
|
The return value from an `@MessageMapping` method may be wrapped with `ListenableFuture`, |
|
|
|
|
`CompletableFuture`, or `CompletionStage` in order to produce the payload asynchronously. |
|
|
|
|
|
|
|
|
|
As an alternative to returning a payload from an `@MessageMapping` method you can also |
|
|
|
|
send messages using the `SimpMessagingTemplate`, which is also how return values are |
|
|
|
|
handled under the covers. See <<websocket-stomp-handle-send>>. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[[websocket-stomp-subscribe-mapping]] |
|
|
|
|
==== `@SubscribeMapping` |
|
|
|
|
|
|
|
|
|
The `@SubscribeMapping` annotation is used in combination with `@MessageMapping` in order |
|
|
|
|
to narrow the mapping to subscription messages. In such scenarios, the `@MessageMapping` |
|
|
|
|
annotation specifies the destination while `@SubscribeMapping` indicates interest in |
|
|
|
|
subscription messages only. |
|
|
|
|
|
|
|
|
|
An `@SubscribeMapping` method is generally no different from any `@MessageMapping` |
|
|
|
|
method with respect to mapping and input arguments. For example you can combine it with a |
|
|
|
|
type-level `@MessageMapping` to express a shared destination prefix, and you can use the |
|
|
|
|
same <<websocket-stomp-message-mapping,method arguments>> as on any @MessageMapping` method. |
|
|
|
|
|
|
|
|
|
The key difference with `@SubscribeMapping` is that the return value of the method is |
|
|
|
|
serialized as a payload and sent, not to the "brokerChannel" but to the |
|
|
|
|
"clientOutboundChannel", effectively replying directly to the client rather than |
|
|
|
|
broadcasting through the broker. This is useful for implementing one-off, request-reply |
|
|
|
|
message exchanges, and never holding on to the subscription. A common scenario for this |
|
|
|
|
pattern is application initialization when data must be loaded and presented. |
|
|
|
|
|
|
|
|
|
A `@SubscribeMapping` method can also be annotated with `@SendTo` in which case the |
|
|
|
|
return value is sent to the `"brokerChannel"` with the explicitly specified target |
|
|
|
|
destination. |
|
|
|
|
|
|
|
|
|
A response message may also be provided asynchronously via a `ListenableFuture` |
|
|
|
|
or `CompletableFuture`/`CompletionStage` return type signature, analogous to |
|
|
|
|
deferred results in an MVC handler method. |
|
|
|
|
|
|
|
|
|
A `@SubscribeMapping` annotation can be used to map subscription requests to |
|
|
|
|
`@Controller` methods. It is supported on the method level, but can also be |
|
|
|
|
combined with a type level `@MessageMapping` annotation that expresses shared |
|
|
|
|
mappings across all message handling methods within the same controller. |
|
|
|
|
[[websocket-stomp-exception-handler]] |
|
|
|
|
==== `@MessageExceptionHandler` |
|
|
|
|
|
|
|
|
|
By default the return value from an `@SubscribeMapping` method is sent as a |
|
|
|
|
message directly back to the connected client and does not pass through the |
|
|
|
|
broker. This is useful for implementing request-reply message interactions; for |
|
|
|
|
example, to fetch application data when the application UI is being initialized. |
|
|
|
|
Or alternatively an `@SubscribeMapping` method can be annotated with `@SendTo` |
|
|
|
|
in which case the resulting message is sent to the `"brokerChannel"` using |
|
|
|
|
the specified target destination. |
|
|
|
|
An application can use `@MessageExceptionHandler` methods to handle exceptions from |
|
|
|
|
`@MessageMapping` methods. Exceptions of interest can be declared in the annotation |
|
|
|
|
itself, or through a method argument if you want to get access to the exception instance: |
|
|
|
|
|
|
|
|
|
[NOTE] |
|
|
|
|
==== |
|
|
|
|
In some cases a controller may need to be decorated with an AOP proxy at runtime. |
|
|
|
|
One example is if you choose to have `@Transactional` annotations directly on the |
|
|
|
|
controller. When this is the case, for controllers specifically, we recommend |
|
|
|
|
using class-based proxying. This is typically the default choice with controllers. |
|
|
|
|
However if a controller must implement an interface that is not a Spring Context |
|
|
|
|
callback (e.g. `InitializingBean`, `*Aware`, etc), you may need to explicitly |
|
|
|
|
configure class-based proxying. For example with `<tx:annotation-driven />`, |
|
|
|
|
change to `<tx:annotation-driven proxy-target-class="true" />`. |
|
|
|
|
==== |
|
|
|
|
[source,java,indent=0] |
|
|
|
|
[subs="verbatim,quotes"] |
|
|
|
|
---- |
|
|
|
|
@Controller |
|
|
|
|
public class MyController { |
|
|
|
|
|
|
|
|
|
// ... |
|
|
|
|
|
|
|
|
|
@MessageExceptionHandler |
|
|
|
|
public ApplicationError handleException(MyException exception) { |
|
|
|
|
// ... |
|
|
|
|
return appError; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
---- |
|
|
|
|
|
|
|
|
|
`@MessageExceptionHandler` methods support flexible method signatures and support the same |
|
|
|
|
method argument types and return values as <<websocket-stomp-message-mapping>> methods. |
|
|
|
|
|
|
|
|
|
Typically `@MessageExceptionHandler` methods apply within the `@Controller` class (or |
|
|
|
|
class hierarchy) they are declared in. If you want such methods to apply more globally, |
|
|
|
|
across controllers, you can declare them in a class marked with `@ControllerAdvice`. |
|
|
|
|
This is comparable to <<web.adoc#mvc-ann-controller-advice,similar support>> in Spring MVC. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|