@@ -1372,82 +1372,146 @@ kinds of arguments and return values supported.
1372
1372
1373
1373
1374
1374
[[websocket-stomp-handle-annotations]]
1375
- === Handler methods
1375
+ === Annotated Controllers
1376
1376
1377
- The `@MessageMapping` annotation is supported on methods of `@Controller` classes.
1378
- It can be used for mapping methods to message destinations and can also be combined
1379
- with the type-level `@MessageMapping` for expressing shared mappings across all
1380
- annotated methods within a controller.
1377
+ Applications can use annotated `@Controller` classes to handle messages from clients.
1378
+ Such classes can declare `@MessageMapping`, `@SubscribeMapping`, and `@ExceptionHandler`
1379
+ methods as described next.
1381
1380
1382
- By default destination mappings are treated as Ant-style, slash-separated, path
1383
- patterns, e.g. "/foo*", "/foo/**". etc. They can also contain template variables,
1384
- e.g. "/foo/{id}" that can then be referenced via `@DestinationVariable`-annotated
1385
- method arguments.
1386
1381
1387
- [NOTE]
1382
+ [[websocket-stomp-message-mapping]]
1383
+ ==== `@MessageMapping`
1384
+
1385
+ The `@MessageMapping` annotation can be used on methods to route messages based on their
1386
+ destination. It is supported at the method level as well as at the type level. At type
1387
+ level `@MessageMapping` is used to express shared mappings across all methods in a
1388
+ controller.
1389
+
1390
+ By default destination mappings are expected to be Ant-style, path patterns, e.g. "/foo*",
1391
+ "/foo/**". The patterns include support for template variables, e.g. "/foo/{id}", that can
1392
+ be referenced with `@DestinationVariable` method arguments.
1393
+
1394
+ [TIP]
1388
1395
====
1389
- Applications can also use dot-separated destinations (vs slash) .
1396
+ Applications can choose to switch to a dot-separated destination convention .
1390
1397
See <<websocket-stomp-destination-separator>>.
1391
1398
====
1392
1399
1393
- The following method arguments are supported for `@MessageMapping` methods:
1394
-
1395
- * `Message` method argument to get access to the complete message being processed.
1396
- * `@Payload`-annotated argument for access to the payload of a message, converted with
1397
- a `org.springframework.messaging.converter.MessageConverter`.
1398
- The presence of the annotation is not required since it is assumed by default.
1399
- Payload method arguments annotated with validation annotations (like `@Validated`) will
1400
- be subject to JSR-303 validation.
1401
- * `@Header`-annotated arguments for access to a specific header value along with
1402
- type conversion using an `org.springframework.core.convert.converter.Converter`
1403
- if necessary.
1404
- * `@Headers`-annotated method argument that must also be assignable to `java.util.Map`
1405
- for access to all headers in the message.
1406
- * `MessageHeaders` method argument for getting access to a map of all headers.
1407
- * `MessageHeaderAccessor`, `SimpMessageHeaderAccessor`, or `StompHeaderAccessor`
1408
- for access to headers via typed accessor methods.
1409
- * `@DestinationVariable`-annotated arguments for access to template
1410
- variables extracted from the message destination. Values will be converted to
1411
- the declared method argument type as necessary.
1412
- * `java.security.Principal` method arguments reflecting the user logged in at
1413
- the time of the WebSocket HTTP handshake.
1414
-
1415
- A return value from an `@MessageMapping` method will be converted with a
1416
- `org.springframework.messaging.converter.MessageConverter` and used as the body
1417
- of a new message that is then sent, by default, to the `"brokerChannel"` with
1418
- the same destination as the client message but using the prefix `"/topic"` by
1419
- default. An `@SendTo` message level annotation can be used to specify any
1420
- other destination instead. It can also be set a class-level to share a common
1400
+ `@MessageMapping` methods can have flexible signatures with the following arguments:
1401
+
1402
+ [cols="1,2", options="header"]
1403
+ |===
1404
+ | Method argument | Description
1405
+
1406
+ | `Message`
1407
+ | For access to the complete message.
1408
+
1409
+ | `MessageHeaders`
1410
+ | For access to the headers within the `Message`.
1411
+
1412
+ | `MessageHeaderAccessor`, `SimpMessageHeaderAccessor`, `StompHeaderAccessor`
1413
+ | For access to the headers via typed accessor methods.
1414
+
1415
+ | `@Payload`
1416
+ | For access to the payload of the message, converted (e.g. from JSON) via a configured
1417
+ `MessageConverter`.
1418
+
1419
+ The presence of this annotation is not required since it is assumed by default if no
1420
+ other argument is matched.
1421
+
1422
+ Payload arguments may be annotated with `@javax.validation.Valid` or Spring's `@Validated`
1423
+ in order to be automatically validated.
1424
+
1425
+ | `@Header`
1426
+ | For access to a specific header value along with type conversion using an
1427
+ `org.springframework.core.convert.converter.Converter` if necessary.
1428
+
1429
+ | `@Headers`
1430
+ | For access to all headers in the message. This argument must be assignable to
1431
+ `java.util.Map`.
1432
+
1433
+ | `@DestinationVariable`
1434
+ | For access to template variables extracted from the message destination.
1435
+ Values will be converted to the declared method argument type as necessary.
1436
+
1437
+ | `java.security.Principal`
1438
+ | Reflects the user logged in at the time of the WebSocket HTTP handshake.
1439
+
1440
+ |===
1441
+
1442
+ When an `@MessageMapping` method returns a value, by default the value is serialized to
1443
+ a payload through a configured `MessageConverter`, and then sent as a `Message` to the
1444
+ `"brokerChannel"` from where it is broadcast to subscribers. The destination of the
1445
+ outbound message is the same as that of the inbound message but prefixed with `"/topic"`.
1446
+
1447
+ You can use the `@SendTo` method annotation to customize the destination to send
1448
+ the payload to. `@SendTo` can also be used at the class level to share a default target
1449
+ destination to send messages to. `@SendToUser` is an variant for sending messages only to
1450
+ the user associated with a message. See <<websocket-stomp-user-destination>> for details.
1451
+
1452
+ The return value from an `@MessageMapping` method may be wrapped with `ListenableFuture`,
1453
+ `CompletableFuture`, or `CompletionStage` in order to produce the payload asynchronously.
1454
+
1455
+ As an alternative to returning a payload from an `@MessageMapping` method you can also
1456
+ send messages using the `SimpMessagingTemplate`, which is also how return values are
1457
+ handled under the covers. See <<websocket-stomp-handle-send>>.
1458
+
1459
+
1460
+ [[websocket-stomp-subscribe-mapping]]
1461
+ ==== `@SubscribeMapping`
1462
+
1463
+ The `@SubscribeMapping` annotation is used in combination with `@MessageMapping` in order
1464
+ to narrow the mapping to subscription messages. In such scenarios, the `@MessageMapping`
1465
+ annotation specifies the destination while `@SubscribeMapping` indicates interest in
1466
+ subscription messages only.
1467
+
1468
+ An `@SubscribeMapping` method is generally no different from any `@MessageMapping`
1469
+ method with respect to mapping and input arguments. For example you can combine it with a
1470
+ type-level `@MessageMapping` to express a shared destination prefix, and you can use the
1471
+ same <<websocket-stomp-message-mapping,method arguments>> as on any @MessageMapping` method.
1472
+
1473
+ The key difference with `@SubscribeMapping` is that the return value of the method is
1474
+ serialized as a payload and sent, not to the "brokerChannel" but to the
1475
+ "clientOutboundChannel", effectively replying directly to the client rather than
1476
+ broadcasting through the broker. This is useful for implementing one-off, request-reply
1477
+ message exchanges, and never holding on to the subscription. A common scenario for this
1478
+ pattern is application initialization when data must be loaded and presented.
1479
+
1480
+ A `@SubscribeMapping` method can also be annotated with `@SendTo` in which case the
1481
+ return value is sent to the `"brokerChannel"` with the explicitly specified target
1421
1482
destination.
1422
1483
1423
- A response message may also be provided asynchronously via a `ListenableFuture`
1424
- or `CompletableFuture`/`CompletionStage` return type signature, analogous to
1425
- deferred results in an MVC handler method.
1426
1484
1427
- A `@SubscribeMapping` annotation can be used to map subscription requests to
1428
- `@Controller` methods. It is supported on the method level, but can also be
1429
- combined with a type level `@MessageMapping` annotation that expresses shared
1430
- mappings across all message handling methods within the same controller.
1485
+ [[websocket-stomp-exception-handler]]
1486
+ ==== `@MessageExceptionHandler`
1431
1487
1432
- By default the return value from an `@SubscribeMapping` method is sent as a
1433
- message directly back to the connected client and does not pass through the
1434
- broker. This is useful for implementing request-reply message interactions; for
1435
- example, to fetch application data when the application UI is being initialized.
1436
- Or alternatively an `@SubscribeMapping` method can be annotated with `@SendTo`
1437
- in which case the resulting message is sent to the `"brokerChannel"` using
1438
- the specified target destination.
1488
+ An application can use `@MessageExceptionHandler` methods to handle exceptions from
1489
+ `@MessageMapping` methods. Exceptions of interest can be declared in the annotation
1490
+ itself, or through a method argument if you want to get access to the exception instance:
1439
1491
1440
- [NOTE]
1441
- ====
1442
- In some cases a controller may need to be decorated with an AOP proxy at runtime.
1443
- One example is if you choose to have `@Transactional` annotations directly on the
1444
- controller. When this is the case, for controllers specifically, we recommend
1445
- using class-based proxying. This is typically the default choice with controllers.
1446
- However if a controller must implement an interface that is not a Spring Context
1447
- callback (e.g. `InitializingBean`, `*Aware`, etc), you may need to explicitly
1448
- configure class-based proxying. For example with `<tx:annotation-driven />`,
1449
- change to `<tx:annotation-driven proxy-target-class="true" />`.
1450
- ====
1492
+ [source,java,indent=0]
1493
+ [subs="verbatim,quotes"]
1494
+ ----
1495
+ @Controller
1496
+ public class MyController {
1497
+
1498
+ // ...
1499
+
1500
+ @MessageExceptionHandler
1501
+ public ApplicationError handleException(MyException exception) {
1502
+ // ...
1503
+ return appError;
1504
+ }
1505
+ }
1506
+ ----
1507
+
1508
+ `@MessageExceptionHandler` methods support flexible method signatures and support the same
1509
+ method argument types and return values as <<websocket-stomp-message-mapping>> methods.
1510
+
1511
+ Typically `@MessageExceptionHandler` methods apply within the `@Controller` class (or
1512
+ class hierarchy) they are declared in. If you want such methods to apply more globally,
1513
+ across controllers, you can declare them in a class marked with `@ControllerAdvice`.
1514
+ This is comparable to <<web.adoc#mvc-ann-controller-advice,similar support>> in Spring MVC.
1451
1515
1452
1516
1453
1517
0 commit comments