I am using HTTP to access the JAVA backend interface, and now, I need to convert the http interface to websocket interface. How to do this, can someone share a sample with me? Thanks.
The http method is like below
httpAction(httpurl,this.model,method).then((res)=>{
if(res.success){
that.$message.success(res.message);
that.$emit('ok');
}else{
that.$message.warning(res.message);
}
}).finally(() => {
that.confirmLoading = false;
})
JAVA backend interface is like this:
#PostMapping(value = "/add")
public Result<?> add(#RequestBody ImportField importField) throws IOException {
....
}
Related
Need some small help about Spring Feign client. So here is the situation,
I have 2 Spring boot services. Let’s say Service A and Service B. I have configured my Service A with Feign client through which I call the Service B method.
So here is the code for my Service A,
My FeignCleint config interface,
#FeignClient(name = "FeignClient", url = "http://localhost:8081/ServiceB/hello")
public interface FeignApi {
#RequestMapping(method = RequestMethod.GET)
ResponseEntity<?> hello();
}
And my rest controller that uses above feign config to call the Service B method,
#RestController
public class ApiController {
#Autowired
private FeignApi feignApi;
#RequestMapping(value = "/callServiceB")
public ResponseEntity<?> companyInfo() {
ResponseEntity<?> response = new ResponseEntity("OK Response", HttpStatus.OK);
try {
response = feignApi.hello();
// Code for some other things related to application.
return response;
} catch (Exception ex) {
System.out.println("Service A Exception block reached.");
return new ResponseEntity(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
}
below is my controller for Service B,
#RestController
public class MyController {
#GetMapping("/hello")
public String hello() throws Exception {
if (true) {
throw new Exception("Service B Exception...");
}
return "Hello World";
}
}
And my Controller advice to handle the exception that I am manually throwing,
#ControllerAdvice
public class MyControllerAdvice {
#ExceptionHandler
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<?> handleException(Exception exception, Model model) {
return new ResponseEntity<>("Caused due to : " + exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Now my flow is like below,
As you can see, I am calling service B from service A using feign client. My service B is throwing an exception manually which I am catching using the controller advice and sending the exception details as an ResponseEntity back to the calling service A. So that Service A can process the details and move forward based on that.
The problem is when I hit the call from Service A using
http://localhost:8080/feign/callServiceB
The service B fails as expected. Now what I expect is that the Service A should receive the response back in form of the ResponseEntity. But what really happens is that the flow enters the exception block instead of staying in the try block. I can see this line printed,
"Service A Exception block reached."
This is what I don't understand. If I have managed the service B exception using controller advice and sent back the response to service A in form of ResponseEntity, then how come the flow of service A enters catch block. I expect it to stay in try block only as I want to process further based on the data.
Any idea, how can I get around this thing? Or is this how it will behave even when I am using controller advice to manage exceptions? What should be the expected behavior in this case?
Please advice.
By default Feign throws FeignException for any error situation.
Make use of fallback mechanism to handle failures.
#FeignClient(name = "FeignClient", url = "http://localhost:8081/ServiceB/hello", fallback= FeignApiFallback.class)
public interface FeignApi {
#RequestMapping(method = RequestMethod.GET)
ResponseEntity<?> hello();
}
#Component
class FeignApiFallback implements FeignApi {
#Override
public ResponseEntity<?> hello() {
//do more logic here
return ResponseEntity.ok().build();
}
}
make sure you add below property to wrap methods in hystrix commands in recent releases
feign.hystrix.enabled=true
Any status other than 200, feign client will consider it as an exception and you are setting HttpStatus.INTERNAL_SERVER_ERROR in your controller advice.
You can use custom ErrorDecoder
refer https://github.com/OpenFeign/feign/wiki/Custom-error-handling
From past few days i'm trying to implement the Spring cloud stream messaging system using RestController, but it is not happening through the current implementation.
For this sample code i'm going to add RestController
#EnableBinding(Source.class)
#EnableConfigurationProperties(TimeSourceOptionsMetadata.class)
public class TimeSource {
#Autowired
private TimeSourceOptionsMetadata options;
#InboundChannelAdapter(value = Source.OUTPUT)
public String timerMessageSource() {
return new SimpleDateFormat(this.options.getFormat()).format(new Date());
}
}
But the #InboundChannelAdapter cannot accept any parameters from RequestMapping Get Method URL.At the end what i need is to add message to the broker using Restful API Get method from api call. which is the best way to do it?, I couldn't figure out any best process from internet.
spring cloud team already provided a source application that listens for HTTP requests and emits the body as a message payload. If the Content-Type matches text/* or application/json, the payload will be a String, otherwise the payload will be a byte array.
github link
You can go with this or if you want to write it yourself, you can do it like below:
#RestController
#EnableBinding(Source.class)
public class RestSource {
#Autowired
private Source channels;
#RequestMapping(path = "/", method = POST, consumes = {"application/json" })
#ResponseStatus(HttpStatus.ACCEPTED)
public void handleRequest(#RequestBody String body, #RequestHeader(HttpHeaders.CONTENT_TYPE) Object contentType) {
sendMessage(body, contentType);
}
private void sendMessage(Object body, Object contentType) {
channels.output().send(MessageBuilder.createMessage(body,
new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType))));
}
}
I have 2 micro-services (Service A and Service B) built using Spring Boot, which gets routed through a Zuul Proxy also built as a Spring Boot app and I have checked that the Zuul proxy works just fine. However, what I am trying to do is to write a custom routing type ZuulFilter which should first route to Service A when a request comes in for Service B. Here is what I need assistance for:
I would like to know an example of how a routing filter looks like as I do not see anything after searching the internet. What I get are some examples of pre-filter and Netflix's documentation doesn't help much as well on that aspect.
Whether writing a custom route filter would mess up the original routing behavior of Zuul
I would construct a Feign client in the Zuul filter and make the call to service A using it. Feign will populate a ribbon load balancer to make the call in just the same way that Zuul does when proxying.
I had the same issue and this is what I came up with.
public class ServicesLegacyRouteFilter extends ZuulFilter {
private ServiceB serviceB;
public ServiceLegacyRouteFilter(ServiceB serviceB) {
this.serviceB = serviceB;
}
#Override
public String filterType() {
return ROUTE_TYPE;
}
#Override
public int filterOrder() {
return 10;
}
#Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
if ("serviceA".equals(ctx.get("serviceId"))) {
//call Service B here and use return type to set
//the final destination service
String destination = serviceB.routeWhere();
ctx.set("serviceId", destination);
return true;
}
return false;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
// Or call ServiceB here to make your determination on
// the final destination.
String destination = serviceB.routeWhere();
ctx.set("serviceId", destination);
return null;
}
}
My actual production use case was more complicated on the routing of course, but this is the basics of how I was able to change routes based on what was coming in and how to take advantage of Zuul to get it out to the correct service.
I'm using stomp.js over SockJS in my javascript client.
I'm connecting to websocket using
stompClient.connect({}, function (frame) {
stomp over sockJS connection has 2 http requests:
request to /info
http upgrade request
the client sends all cookies. I would like to also send custom headers (e.g. XSRF header) but didn't find a way to do that. Will appreciate any help.
#Rohitdev
So basically you can't send any HTTP headers using stompClient, because STOMP is layer over websockets, and only when websockets handshake happen we have possibility to send custom headers.
So only SockJS can send this headers, but for some reasons don't do this: https://github.com/sockjs/sockjs-client/issues/196
Custom headers:
stompClient.connect({token: "ABC123"}, function(frame) { ... code ...});
Without Custom headers:
stompClient.connect({}, function(frame) { ... code ...});
In Javascript, you can extract an STOMP header using:
username = frame.headers['user-name'];
In the server side, if you are using Spring Framework you can implementa an Interceptor to copy the HTTP parmeters to WebSockets STOMP headers.
public class HttpSessionHandshakeInterceptor_personalised implements HandshakeInterceptor {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
// Set ip attribute to WebSocket session
attributes.put("ip", request.getRemoteAddress());
// ============================================= CODIGO PERSONAL
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpServletRequest httpServletRequest = servletRequest.getServletRequest();
// httpServletRequest.getCookies();
// httpServletRequest.getParameter("inquiryId");
// httpServletRequest.getRemoteUser();
String token = httpServletRequest.getParameter("token");
...
}
}
And for send messages without STOMP parameters:
function sendMessage() {
var from = document.getElementById('from').value;
var text = document.getElementById('text').value;
stompClient.send("/app/chatchannel", {},
JSON.stringify({'from':from, 'text':text}));
}
and here you are passing parameters into the STOMP headers.
function sendMessage() {
var from = document.getElementById('from').value;
var text = document.getElementById('text').value;
stompClient.send("/app/chatchannel", {'token':'AA123'},
JSON.stringify({'from':from, 'text':text}));
}
Use #Header(name = "token") annotation inside the method if you are using Spring boot at the server.
Usage -
#Controller
public class SocketController {
static final String token = "1234";
#MessageMapping("/send")
#SendTo("/receive/changes")
public Object notify(MessageModel message, #Header(name = "token") String header)throws Exception {
if(!header.equals(token)) {
// return when headers do not match
return("Unauthorized");
}
// return the model object with associated sent message
return new MessageModel(message.getMessage());
}
}
You should have a MessageModel class with message variable and required getters, setters and contructor.
In frontend use Stomp
Usage -
function sendMessage() {
var text = document.getElementById('text').value;
stompClient.send("/send/message", {'token':'1234'},
JSON.stringify({'message':text}));
}
To add more security you an use CORS in Spring
You must use query as parameter instead to use Authorization in Header.
(?query=token_2222)
example: var socket = new SockJS('/ws?query=token_2222');
then read it in HandshakeInterceptor as Sergio wrote
SockJS JavaScript client does not support sending authorization header with a SockJS request.
Spring Java’s STOMP client allows to set headers for the handshake:
WebSocketHttpHeaders handshakeHeaders = new WebSocketHttpHeaders();
handshakeHeaders.add(principalRequestHeader, principalRequestValue);
Additional information: https://www.toptal.com/java/stomp-spring-boot-websocket
And you can find a lot of infоrmation about this point on Spring documentation:
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#websocket-stomp-authentication
Short conclusion: for applications using cookies, integration is very good (Spring Security and other).
For applications using JWT, possible options are:
1. Add as a request parameter and process in the implementation of DefaultHandshakeHandler
var socket = new SockJS('/our-websocket?query=token_2222');
2. OR add directly to the STOMP header of the message:
//add
var headers = {
login: 'mylogin',
passcode: 'mypasscode',
// additional header
'client-id': 'my-client-id'
};
stompClient.connect(headers, function (frame) {}
//Place of processing
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(final MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/topic")
.setRelayHost("127.0.0.1")
.setRelayPort(61613) //rabbitmq-plugins enable rabbitmq_stomp ; docker exec -it ID bash
.setClientLogin("guest")
.setClientPasscode("guest")
.setUserRegistryBroadcast("/topic/registry") //позволяет отправлять сообщеня всем приватные сообщения всем юзерам
;
registry.setApplicationDestinationPrefixes("/ws");
}
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/our-websocket")
.setHandshakeHandler(new UserHandshakeHandler())
.setAllowedOriginPatterns("*")
.withSockJS()
;
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
// 1) Perform authentication here using standard authentication providers (managers).
//2) Install the user in case of successful authentication or throw an error
accessor.setUser(new UserPrincipal("TEST USER 2"));
}
return message;
}
});
}
}
Problem: I can not get RestEasy to automatically follow redirects
I'm using the RestEasy client framework 2.3.4 to consume RESTful JSON services. I'm using the rest easy client spring integration. If I wasn't using spring RestClientProxyFactoryBean to create my services I would set the auto redirect flag on the client request factory
I have tried setting the follow redirect on my HTTP client and following the debug I can see this value is overridden to false by Rest Easy.
Looking at the source code I need to get access to the client invoker that the spring proxy factory creates but it doesn't expose this.
This is like a very common task, surely I am missing something? Cheers.
You should be able to set a custom client executor on the proxybean factory but that also didn't work e.g
#Override
public ClientRequest createRequest(String uriTemplate) {
ClientRequest clientRequest = new ClientRequest(uriTemplate, this);
clientRequest.followRedirects(true);
return clientRequest;
}
#Override
public ClientRequest createRequest(UriBuilder uriBuilder) {
ClientRequest clientRequest = super.createRequest(uriBuilder);
clientRequest.followRedirects(true);
return clientRequest;
}
}
proxyFactoryBean.setClientExecutor(new FollowRedirectsClientExecutor());
In end extending and overriding the Http client (in this case HTTP Component) was needed to make this work e.g.
public HttpUriRequest followRedirects(HttpUriRequest request) {
if (logger.isDebugEnabled()) {
logger.debug("Setting allow redirects");
}
HttpParams p = request.getParams();
HttpClientParams.setRedirecting(p, true);
request.setParams(p);
return request;
}
}
...
#Override
public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throw
s IOException,
ClientProtocolException { ClientProtocolException {
request = followRedirects(request);
...