From 3b4c7a40c0676b1013c20d69be535230e7e68433 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 23 Mar 2018 10:54:53 -0400 Subject: [PATCH] Improve docs on @Controller methods in STOMP section Issue: SPR-16631 --- src/docs/asciidoc/web/websocket.adoc | 192 ++++++++++++++++++--------- 1 file changed, 128 insertions(+), 64 deletions(-) diff --git a/src/docs/asciidoc/web/websocket.adoc b/src/docs/asciidoc/web/websocket.adoc index c19407fb6e..cbe8a4d138 100644 --- a/src/docs/asciidoc/web/websocket.adoc +++ b/src/docs/asciidoc/web/websocket.adoc @@ -1205,82 +1205,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 <>. ==== -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 <> 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-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 <> 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 ``, -change to ``. -==== +[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 <> 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 <> in Spring MVC. -- GitLab