Recent Version of Spring WebSocket works with SockJS and StompJS libraries. But i don't like to use theme in my application. So how to create Spring WebSocket application with HTML5 WebSocket API and integrate our application with Spring Security?
I could not find any good example on how to configure spring websocket without sockjs but i found some helpful documentation in spring documentation site and i like to share that. Well, How to Create Spring WebSocket Application With HTML5 WebSocket API?
First: Create a Class that extends TextWebSocketHandler or BinaryWebSocketHandler and Annotate it with #Component annotation and Override its appropriate method.This Class works like handler methods in controllers.
#Component
public class SimpleWebSocketHandler extends TextWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session,
TextMessage message) throws Exception {
// Sends back response to client.
session.sendMessage(new TextMessage("Connection is all right."));
}
}
Second: Create a Configuration Class that implements WebSocketConfigurer and Annotate it with #Configuration and #EnableWebSocket annoations and Override its appropriate method.This Class uses Handler Class that we created already.
#Configuration
#EnableWebSocket
public class WebSocketConfigurations implements WebSocketConfigurer {
#Autowired
private SimpleWebSocketHandler simpleWebSocketHandler;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// Regsiters a handler for related endpoint.
registry.addHandler(simpleWebSocketHandler, "/chat");
}
}
Third: Add all your WebSokcet Endpoints to your Spring Security Configuration.
httpSecurity.authorizeRequests()
.antMatchers("/chat").permitAll();
Fourth: We create a new javascript WebSocket objet with appropriate URL.
// Create WebSocket Object.
var ws = new WebSocket("ws://localhost:8080/chat");
// Runs when connecion is estabilished.
ws.onopen = function () {
// Sends request to server with string value.
ws.send("webSocket");
};
// Runs when response is ready.
// Use event to get response value.
ws.onmessage = function (event) {
};
Note: WebSocket URLs Format: ws://domain:port/endpoint
Related
I try to create simple chat via WebSockets. I have configuration class which register appropriate endpoints and I try to create service which will save user id which is generated in handshake service. The problem is that I cannot inject any services to the handshake service so I decided to create class as below:
#Slf4j
#RequiredArgsConstructor
public class WebSocketEventListener {
#EventListener
public void handleWebSocketConnectListener(SessionConnectEvent event) {
log.debug("Handled connection event");
}
#EventListener
public void handleWebSocketConnectedListener(SessionConnectedEvent event) {
log.debug("Handled connected event");
}
#EventListener
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
log.debug("Handled disconnection event");
}
}
Register it as a Bean in configuration class:
#Bean
public WebsocketEventListener websocketEventListener() {
return new WebsocketEventListener();
}
And there try to save user id into database.
Unfortunately when I connect to the webSocket via android app, Spring doesn't detect any event connected with Connection session but when I close the connection between android app and spring server I'm getting log from handleWebSocketDisconnectListener method.
I also tried to add annotation #Component to the class WebSocketEventListener instead of registering it as a Bean but I got the same situation.
I tried to implement ApplicationListener and register it in META-INF folder but also without any results.
P.S. I use Spring Boot version: 2.6.4 and Java 17
I don't know what I'm doing wrong. Any suggestions?
I am using Spring Boot 2 just to try some reactive programming with Spring 5. I created some standard MVC controller.
#RestController
#RequestMapping("/judge/rest")
public class BasicController {
private static final Logger LOGGER = LoggerFactory.getLogger(BasicController.class);
#GetMapping("/hello")
public Mono<String> handle() {
LOGGER.debug("Invoking hello controller");
return Mono.just("Hello WebFlux");
}
}
And standard router function.
#Configuration
public class WebConfig {
#Bean
public RouterFunction<?> helloRoute() {
return route(GET("/judge/router/hello"),
request -> ServerResponse.ok().body(fromPublisher(Mono.just("Hello Router WebFlux"), String.class)));
}
}
My main spring boot application looks like this
#SpringBootApplication
public class JudgeRuleEngineApplication {
public static void main(String[] args) {
SpringApplication.run(JudgeRuleEngineApplication.class, args);
}
}
But in documentation for spring 5 I ran into
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
ReactorHttpHandlerAdapter adapter =
new ReactorHttpHandlerAdapter(httpHandler);
HttpServer server = HttpServer.create("localhost", 8080);
server.startAndAwait(adapter);
It seems that server is intantiated manually.
My question is when should I instantiate the server like this? Because so far it seems with #SpringBootApplication and main it handles requests just fine.
As the document says
Now there is just one piece of the puzzle missing: running a router
function in an HTTP server. You can convert a router function into a
HttpHandler by using RouterFunctions.toHttpHandler(RouterFunction).
The HttpHandler allows you to run on a wide variety of reactive
runtimes: Reactor Netty, RxNetty, Servlet 3.1+, and Undertow.
Which means the above code which you have shown, uses Reactor Netty as the reactive runtime. If you wish to use any other runtimes which has reactive native adapter, you can do so. In such cases you would instantiate the server like this.
By default Spring boot default to Reactor Netty.
In my Spring Boot application I'm trying to implement a notifications functionality based on WebSockets.
I have provided a following configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/notifications").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/queue");
}
}
and trying to use SimpMessagingTemplate in order to send a message from server side to a specific client(user).
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
public void sendMessages() {
simpMessagingTemplate.convertAndSendToUser(%user%, "/horray", "Hello, World!");
}
Right now I don't understand a few things:
What value should be used for %user% parameter of
simpMessagingTemplate.convertAndSendToUser method ?
What is the correlation between my /notifications endpoint
registered in WebSocketConfig.registerStompEndpoints method and
destination parameter of
simpMessagingTemplate.convertAndSendToUser method and how to properly use it?
How to protect the users from reading other people's messages on the
client ?
The user parameter is the name that the client use when he subscribes the destination, see Spring Reference Chapter 26.4.11 User Destinations
Destination vs Endpoint:
Endpoint is the url where the websocket/message brocker is listening
Destination is the topic or subject within the message brocker
I am trying to add an interceptor to a simple Spring-boot-mongodb-rest app, as can be seen here : http://spring.io/guides/gs/accessing-mongodb-data-rest/, in order to perform certain actions after the default rest handler is invoked. Here is my MongoRepository, whose CRUD operation is called upon a POST request to the server:
#RepositoryRestResource(collectionResourceRel = "reminder", path = "reminder")
public interface ReminderRepository extends MongoRepository<Reminder, String> {
List<Reminder> findBySendee(#Param("sendee") String sendee);
}
I am trying to register an interceptor for all HTTP requests by extending the WebMvcConfigurerAdapter class like this:
#Configuration
#ComponentScan
public class RemindxWebConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new RemindxInterceptor());
}
}
As mentioned in the spring boot docs, I have not added the #EnableWebMvc annotation to this. While running the application, the addInterceptors function does get called and adds the interceptor. However, the given interceptor is not called after the POST handler is invoked. I am unable to figure out a way to have spring use this RemindxWebConfig for all MongoRepository http requests. Any inputs are appreciated.
I have a use case where my application hosts REST API and web application and we need to add custom header to REST APIs only. REST APIs are enabled through Spring Data REST. Typically we could use Servlet Filter to achieve this but we need code the logic of isolating requests to our REST API and add the custom headers. It would be nice if Spring Data REST API allows to add a default header to all the responses it generates. What are your thoughts? Don't say I am lazy :)
For folks looking for actual implementation details..
Interceptor
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("adding CORS headers.....");
response.addHeader("HEADER-NAME", "HEADER-VALUE");
return true;
}
}
Java Configuration
#Configuration
public class RepositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
public RequestMappingHandlerMapping repositoryExporterHandlerMapping() {
RequestMappingHandlerMapping mapping = super
.repositoryExporterHandlerMapping();
mapping.setInterceptors(new Object[] { new CustomInterceptor() });
return mapping;
}
}
As Spring Data REST is built on top of Spring MVC, the easiest way is to configure a custom HandlerInterceptor as described in the reference documentation.
With Spring Data REST the easiest way is to extend RepositoryRestMvcConfiguration and override repositoryExporterHandlerMapping, call the parent method and then invoke ….setInterceptors(…) on it.
Finally I managed to make the setup of custom interceptor working also on spring-data-rest 2.4.1.RELEASE.
#Configuration
public class RestMvcConfig extends RepositoryRestMvcConfiguration {
#Autowired UserInterceptor userInterceptor;
#Autowired ApplicationContext applicationContext;
#Override
public DelegatingHandlerMapping restHandlerMapping() {
RepositoryRestHandlerMapping repositoryMapping = new RepositoryRestHandlerMapping(resourceMappings(), config());
repositoryMapping.setInterceptors(new Object[] { userInterceptor }); // FIXME: not nice way of defining interceptors
repositoryMapping.setJpaHelper(jpaHelper());
repositoryMapping.setApplicationContext(applicationContext);
repositoryMapping.afterPropertiesSet();
BasePathAwareHandlerMapping basePathMapping = new BasePathAwareHandlerMapping(config());
basePathMapping.setApplicationContext(applicationContext);
basePathMapping.afterPropertiesSet();
List<HandlerMapping> mappings = new ArrayList<HandlerMapping>();
mappings.add(basePathMapping);
mappings.add(repositoryMapping);
return new DelegatingHandlerMapping(mappings);
}
}
I had to override the restHandlerMapping method, copy-paste it's content and add a line repositoryMapping.setInterceptors for adding custom interceptor, in my case the UserInterceptor.
Is there any better way?