Erweiterungen

This commit is contained in:
2025-08-29 14:43:10 +02:00
parent 4d2ed39275
commit 846b3999f2
5 changed files with 259 additions and 9 deletions

View File

@@ -32,14 +32,70 @@ Das System bietet folgende STOMP-Funktionalitäten:
```javascript ```javascript
// Mit SockJS // Mit SockJS
const socket = new SockJS('http://localhost:8080/ws'); const socket = new SockJS('http://192.168.180.196:8080/ws');
const stompClient = Stomp.over(socket); const stompClient = Stomp.over(socket);
// Oder mit nativem WebSocket // Oder mit nativem WebSocket (WICHTIG: ws:// verwenden, nicht http://)
const socket = new WebSocket('ws://localhost:8080/websocket'); const socket = new WebSocket('ws://192.168.180.196:8080/websocket');
const stompClient = Stomp.over(socket); const stompClient = Stomp.over(socket);
``` ```
### Flutter/Dart STOMP Client
**Für Flutter-Apps verwenden Sie diese Konfiguration:**
```dart
import 'package:stomp_dart_client/stomp.dart';
import 'package:stomp_dart_client/stomp_config.dart';
import 'package:stomp_dart_client/stomp_frame.dart';
import 'dart:convert';
// WICHTIG: Verwenden Sie ws:// für WebSocket-Verbindungen, NICHT http://
final stompClient = StompClient(
config: StompConfig(
url: 'ws://192.168.180.196:8080/websocket', // Beachten Sie ws:// statt http://
onConnect: onConnectCallback,
onWebSocketError: (dynamic error) => print('WebSocket Error: $error'),
onStompError: (StompFrame frame) => print('Stomp Error: ${frame.body}'),
onDisconnect: (StompFrame frame) => print('Disconnected'),
// Heartbeat-Konfiguration
heartbeatIncoming: Duration(seconds: 20),
heartbeatOutgoing: Duration(seconds: 20),
),
);
void onConnectCallback(StompFrame frame) {
print('Connected to STOMP server');
// Nachrichten abonnieren
stompClient.subscribe(
destination: '/topic/messages',
callback: (StompFrame frame) {
print('Received message: ${frame.body}');
},
);
}
// Verbindung herstellen
stompClient.activate();
// Nachricht senden
void sendMessage(String content) {
stompClient.send(
destination: '/app/message',
body: jsonEncode({
'content': content,
'sender': 'FlutterApp',
}),
);
}
```
**Alternative Endpunkte für Flutter:**
Falls Probleme mit `/websocket` auftreten, versuchen Sie:
- `ws://192.168.180.196:8080/stomp` (zusätzlicher Endpunkt)
- `ws://192.168.180.196:8080/ws` (SockJS-Endpunkt ohne SockJS-Protokoll)
### 2. Verbindung herstellen ### 2. Verbindung herstellen
```javascript ```javascript

159
flutter_websocket_test.html Normal file
View File

@@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flutter WebSocket Test</title>
<script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@7.0.0/bundles/stomp.umd.min.js"></script>
</head>
<body>
<h1>Flutter WebSocket STOMP Test</h1>
<div id="status">Nicht verbunden</div>
<button onclick="connectWebSocket()">WebSocket Verbinden</button>
<button onclick="connectSockJS()">SockJS Verbinden</button>
<button onclick="disconnect()">Trennen</button>
<br><br>
<input type="text" id="messageInput" placeholder="Nachricht eingeben" />
<button onclick="sendMessage()">Nachricht senden</button>
<br><br>
<div id="messages"></div>
<script>
let stompClient = null;
function connectWebSocket() {
// WICHTIG: Für native WebSocket MUSS ws:// verwendet werden, NICHT http://
const socket = new WebSocket('ws://192.168.180.196:8080/websocket');
stompClient = Stomp.over(socket);
stompClient.debug = function(str) {
console.log('STOMP Debug: ' + str);
addMessage('DEBUG: ' + str);
};
stompClient.connect({}, function(frame) {
console.log('WebSocket Connected: ' + frame);
document.getElementById('status').innerHTML = 'WebSocket Verbunden';
addMessage('WebSocket erfolgreich verbunden');
// Nachrichten abonnieren
stompClient.subscribe('/topic/messages', function(message) {
addMessage('Empfangen: ' + message.body);
});
}, function(error) {
console.error('WebSocket Connection Error:', error);
document.getElementById('status').innerHTML = 'WebSocket Fehler: ' + error;
addMessage('WebSocket Fehler: ' + error);
});
}
function connectSockJS() {
// Für SockJS kann http:// verwendet werden
const socket = new SockJS('http://192.168.180.196:8080/ws');
stompClient = Stomp.over(socket);
stompClient.debug = function(str) {
console.log('STOMP Debug: ' + str);
addMessage('DEBUG: ' + str);
};
stompClient.connect({}, function(frame) {
console.log('SockJS Connected: ' + frame);
document.getElementById('status').innerHTML = 'SockJS Verbunden';
addMessage('SockJS erfolgreich verbunden');
// Nachrichten abonnieren
stompClient.subscribe('/topic/messages', function(message) {
addMessage('Empfangen: ' + message.body);
});
}, function(error) {
console.error('SockJS Connection Error:', error);
document.getElementById('status').innerHTML = 'SockJS Fehler: ' + error;
addMessage('SockJS Fehler: ' + error);
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
document.getElementById('status').innerHTML = 'Getrennt';
addMessage('Verbindung getrennt');
}
}
function sendMessage() {
const messageInput = document.getElementById('messageInput');
if (stompClient && messageInput.value) {
stompClient.send('/app/message', {}, JSON.stringify({
'content': messageInput.value,
'sender': 'WebTest'
}));
addMessage('Gesendet: ' + messageInput.value);
messageInput.value = '';
}
}
function addMessage(message) {
const messagesDiv = document.getElementById('messages');
const messageElement = document.createElement('div');
messageElement.innerHTML = new Date().toLocaleTimeString() + ': ' + message;
messagesDiv.appendChild(messageElement);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
</script>
<h2>Flutter Dart Code Beispiel:</h2>
<pre><code>
import 'package:stomp_dart_client/stomp.dart';
import 'package:stomp_dart_client/stomp_config.dart';
import 'package:stomp_dart_client/stomp_frame.dart';
import 'dart:convert';
// RICHTIG: ws:// für WebSocket verwenden
final stompClient = StompClient(
config: StompConfig(
url: 'ws://192.168.180.196:8080/websocket', // ws:// NICHT http://
onConnect: (StompFrame frame) {
print('Connected to STOMP server');
// Nachrichten abonnieren
stompClient.subscribe(
destination: '/topic/messages',
callback: (StompFrame frame) {
print('Received: ${frame.body}');
},
);
},
onWebSocketError: (dynamic error) => print('WebSocket Error: $error'),
onStompError: (StompFrame frame) => print('Stomp Error: ${frame.body}'),
onDisconnect: (StompFrame frame) => print('Disconnected'),
),
);
// Verbindung aktivieren
stompClient.activate();
// Nachricht senden
void sendMessage(String content) {
stompClient.send(
destination: '/app/message',
body: jsonEncode({
'content': content,
'sender': 'FlutterApp',
}),
);
}
</code></pre>
<h2>Verfügbare Endpunkte:</h2>
<ul>
<li><strong>ws://192.168.180.196:8080/websocket</strong> - Native WebSocket (empfohlen für Flutter)</li>
<li><strong>ws://192.168.180.196:8080/stomp</strong> - Alternative WebSocket Endpunkt</li>
<li><strong>http://192.168.180.196:8080/ws</strong> - SockJS Endpunkt (nur für Browser)</li>
</ul>
<h2>Häufiger Fehler:</h2>
<p><strong>FALSCH:</strong> <code>http://192.168.180.196:8080/websocket</code></p>
<p><strong>RICHTIG:</strong> <code>ws://192.168.180.196:8080/websocket</code></p>
</body>
</html>

View File

@@ -5,6 +5,7 @@ import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
/** /**
* WebSocket configuration for STOMP messaging. * WebSocket configuration for STOMP messaging.
@@ -23,15 +24,32 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
// Designate the "/app" prefix for messages that are bound to methods // Designate the "/app" prefix for messages that are bound to methods
// annotated with @MessageMapping // annotated with @MessageMapping
config.setApplicationDestinationPrefixes("/app"); config.setApplicationDestinationPrefixes("/app");
// Set user destination prefix for user-specific messages
config.setUserDestinationPrefix("/user");
} }
@Override @Override
public void registerStompEndpoints(StompEndpointRegistry registry) { public void registerStompEndpoints(StompEndpointRegistry registry) {
// Register the "/ws" endpoint for WebSocket connections // Register the "/ws" endpoint for WebSocket connections with SockJS fallback
// withSockJS() enables SockJS fallback options for browsers that don't support WebSocket registry.addEndpoint("/ws")
registry.addEndpoint("/ws").withSockJS(); .setAllowedOriginPatterns("*")
.addInterceptors(new HttpSessionHandshakeInterceptor())
.withSockJS()
.setHeartbeatTime(25000) // Set heartbeat interval
.setDisconnectDelay(5000) // Set disconnect delay
.setStreamBytesLimit(128 * 1024) // Set stream bytes limit
.setHttpMessageCacheSize(1000) // Set HTTP message cache size
.setSessionCookieNeeded(false); // Disable session cookie requirement
// Also add a plain WebSocket endpoint without SockJS for native WebSocket clients // Plain WebSocket endpoint without SockJS for native WebSocket clients (Flutter, mobile apps)
registry.addEndpoint("/websocket"); registry.addEndpoint("/websocket")
.setAllowedOriginPatterns("*")
.addInterceptors(new HttpSessionHandshakeInterceptor());
// Additional endpoint specifically for mobile/Flutter clients that might have URL issues
registry.addEndpoint("/stomp")
.setAllowedOriginPatterns("*")
.addInterceptors(new HttpSessionHandshakeInterceptor());
} }
} }

View File

@@ -37,10 +37,26 @@ public class SecurityConfig extends VaadinWebSecurity {
new AntPathRequestMatcher("/frontend/**"), new AntPathRequestMatcher("/frontend/**"),
new AntPathRequestMatcher("/webjars/**"), new AntPathRequestMatcher("/webjars/**"),
new AntPathRequestMatcher("/h2-console/**"), new AntPathRequestMatcher("/h2-console/**"),
new AntPathRequestMatcher("/frontend-es5/**", "/frontend-es6/**") new AntPathRequestMatcher("/frontend-es5/**", "/frontend-es6/**"),
// WebSocket und STOMP Endpunkte
new AntPathRequestMatcher("/ws/**"),
new AntPathRequestMatcher("/websocket/**"),
new AntPathRequestMatcher("/stomp/**"),
new AntPathRequestMatcher("/app/**"),
new AntPathRequestMatcher("/topic/**"),
new AntPathRequestMatcher("/queue/**")
).permitAll() ).permitAll()
); );
// CSRF für WebSocket-Endpunkte deaktivieren
http.csrf(csrf -> csrf
.ignoringRequestMatchers(
new AntPathRequestMatcher("/ws/**"),
new AntPathRequestMatcher("/websocket/**"),
new AntPathRequestMatcher("/stomp/**")
)
);
// Delegiere die Basis-Konfiguration an VaadinWebSecurity // Delegiere die Basis-Konfiguration an VaadinWebSecurity
// Dies fügt automatisch .anyRequest().authenticated() hinzu // Dies fügt automatisch .anyRequest().authenticated() hinzu
super.configure(http); super.configure(http);

View File

@@ -1,4 +1,5 @@
server.port=${PORT:8080} server.port=${PORT:8080}
server.address=0.0.0.0
logging.level.org.atmosphere=warn logging.level.org.atmosphere=warn
spring.mustache.check-template-location=false spring.mustache.check-template-location=false