Die klassische Kommunikation zwischen JavaScript Anwendung und Backend findet zustandslos getriggert durch den Client statt. Doch was, wenn unser Backend “wichtige Neuigkeiten” für unser Frontend hat?
“Polling” mag dem ein oder anderen Pragmatiker dazu einfallen. Dabei sendet der Client in festgelegten Intervallen entsprechende Requests an unser Backend, um festzustellen, ob es neue Daten gibt. Technisch eine einfache Lösung, die jedoch dazu führt, dass (insbesondere bei steigenden Userzahlen) unnötig viele Requests von unserem Server beantwortet werden müssen.
WebSockets dagegen stellen eine konstante bidirektionale Verbindung zwischen Server und Client dar und ermöglichen es uns Nachrichten in Echtzeit auszutauschen. In Spring Boot benötigen wir dazu eine zusätzliche Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Innerhalb unserer Spring Boot Anwendung werden wir in den meisten Fällen zusätzlich STOMP einsetzen. Stomp ist ein weit verbreitetes Nachrichtenprotokoll, das out-of-the-box von Spring unterstützt wird und das speziell für Skriptsprachen entwickelt wurde und somit auch eine einfache Integration in unsere Angular Anwendung bietet.
Der erste Schritt besteht nun die gewünschten Websocket Endpunkte zu konfigurieren:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/notifier");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOriginPatterns("*");
}
/ws ist der Endpunkt unter dem Websocket Verbindungen zugelassen werden.
/notifier ist der Pfadpräfix unter dem sich Clients registrieren, um Nachrichten von unserem Backend zu empfangen, z.B. http://…/ws/notifier/messages
/app ist der Pfadpräfix den die Clients dazu verwenden können Nachrichten an das Backend zu schicken, z.B. http://…/ws/app/new-message
Empfangen
In unseren Controllern können wir nun, ähnlich wie bei klassischen Rest-Endpunkten, Methoden annotieren, um auf Nachrichten zu reagieren
@Controller
@RequiredArgsConstructor
public class MessageController {
private final MessageDummyService messageDummyService;
@MessageMapping("/message")
public void addMessage(MessageDto message) {
messageDummyService.addMessage(message);
}
}
Zusätzlichen könnten wir auch mittels @SendTo(“/notifier/new-message”) und einem entsprechenden Rückgabe-Objekt direkt mittels WebSocket Nachricht antworten
Senden
Innerhalb eines Services lassen sich Nachrichten bequem per Messaging Template absetzen
@Service
@RequiredArgsConstructor
public class MessageDummyServiceImpl implements MessageDummyService {
private final SimpMessagingTemplate simpMessagingTemplate;
private void sendMessage() {
simpMessagingTemplate.convertAndSend("/notifier/message", new MessageDto("Lorem Ipsum", Instant.now()));
}
}
…aber bitte mit JSON
Damit unser Nachrichtenaustausch über WebSockets analog zu unseren Rest-Endpunkten mit JSON Formaten arbeitet, ergänzen wir unsere WebSocketConfig oben um eine zusätzliche Konfiguration
@Override
public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
DefaultContentTypeResolver resolver = new DefaultContentTypeResolver();
resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON);
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setObjectMapper(objectMapper);
converter.setContentTypeResolver(resolver);
messageConverters.add(converter);
return false;
}
Das war’s erst einmal schon für unser Backend.
Im nächsten Teil schauen wir uns die Gegenseite, unsere Angularanwendung, an.
Alles Live und In Farbe: https://github.com/GEDOPLAN/spring-angular-websocket