Issue
I am developing a websocket locally with Spring boot and Angular 12. I have been reading a lot of tutorials but I am having a weird Cors error.
The Angular code to connect to the socket is the following:
import { Injectable } from '@angular/core';
import { NotificationWebSocket } from '../model/NotificationWebSocket.model';
import { Stomp } from '@stomp/stompjs';
import * as SockJS from 'sockjs-client';
@Injectable({
providedIn: 'root'
})
export class WebsocketService{
stompClient: any;
webSocketEndPoint: string = 'http://localhost:8080/notification';
topic : string = "/notifications/messages"
constructor(){
console.log("Initialize WebSocket Connection");
let ws = new SockJS(this.webSocketEndPoint);
this.stompClient = Stomp.over(ws);
const _this = this;
_this.stompClient.connect({}, function (frame : any) {
_this.stompClient.subscribe(_this.topic, function (sdkEvent : any) {
_this.onMessageReceived(sdkEvent);
});
},);
}
disconnect() {
if (this.stompClient !== null) {
this.stompClient.disconnect();
}
console.log("Disconnected");
}
onMessageReceived(message : NotificationWebSocket) {
console.log("Message Recieved from Server :: " + message);
}
}
This code is initialized in a component loaded when the page is loaded
In the backend of spring boot I have the following websocket configuration
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/notification");
config.setApplicationDestinationPrefixes("/wigstat");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/notifications").setAllowedOrigins( "*" ).withSockJS();
}
}
@Controller
public class WebSockerSender {
@Autowired
private NotificationWebSocketAdapter notificationWebSocketAdapter;
@MessageMapping("/updates")
@SendTo("/notifications/messages")
public NotificationWebSocket send(Notification notification) {
return notificationWebSocketAdapter.fromEntityToWebSocket( notification );
}
}
As you can see it is a super simple configuration that all tutorials shows.
for CORS purpose for normal rest endpoints I have a Cors filter in backend configuration
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
res.setHeader( "Access-Control-Allow-Origin", "*" );
res.setHeader( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS" );
res.setHeader( "Access-Control-Allow-Headers", "*" );
// Just REPLY OK if request method is OPTIONS for CORS (pre-flight)
if ( req.getMethod().equals("OPTIONS") ) {
res.setHeader( "Access-Control-Max-Age", "86400" );
res.setStatus( HttpServletResponse.SC_OK );
return;
}
chain.doFilter( request, response );
}
@Override
public void destroy() { }
@Override
public void init(FilterConfig filterConfig) { }
}
When I run the front end and I check the console logs i see the following:
I dont understand why i can not connect with this super simple configuration.
The Rest enpoints defined with logic as rest controllers works perfectly
Thanks in advance
If i change the cors filter or websocket configuration like this
res.setHeader( "Access-Control-Allow-Origin", "http://localhost:4200" );
and
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/notifications").setAllowedOrigins( "http://localhost:4200" ).withSockJS();
}
I get the same error
FIX
I change the cors filter to this and also add
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
String origin = req.getHeader("Origin");
res.setHeader("Access-Control-Allow-Origin", allowedOrigins.contains(origin) ? origin : "*");
res.setHeader( "Access-Control-Allow-Credentials", "true" );
res.setHeader("Vary", "Origin");
res.setHeader( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS" );
res.setHeader( "Access-Control-Allow-Headers", "*" );
// Just REPLY OK if request method is OPTIONS for CORS (pre-flight)
if ( req.getMethod().equals("OPTIONS") ) {
res.setHeader( "Access-Control-Max-Age", "86400" );
res.setStatus( HttpServletResponse.SC_OK );
return;
}
chain.doFilter( request, response );
}
.setAllowedOrigins("http://localhost:4200") in the configuration. Now I have to figure out how to configure that for all different environments, not only locally
Solution
FIX
I change the cors filter to this and also add
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
String origin = req.getHeader("Origin");
res.setHeader("Access-Control-Allow-Origin", allowedOrigins.contains(origin) ? origin : "*");
res.setHeader( "Access-Control-Allow-Credentials", "true" );
res.setHeader("Vary", "Origin");
res.setHeader( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS" );
res.setHeader( "Access-Control-Allow-Headers", "*" );
// Just REPLY OK if request method is OPTIONS for CORS (pre-flight)
if ( req.getMethod().equals("OPTIONS") ) {
res.setHeader( "Access-Control-Max-Age", "86400" );
res.setStatus( HttpServletResponse.SC_OK );
return;
}
chain.doFilter( request, response );
}
setAllowedOrigins("http://localhost:4200") in the configuration. Now I have to figure out how to configure that for all different environments, not only locally
Answered By - Gabriel García Garrido