When using Spring MVC and Spring Cloud Eureka, how can I get Spring MVC to resolve my redirect URLs similar to how RestTemplate is able to?
For example, if I have two services, user-service and movie-service that are both registered with Spring Cloud Eureka, how can I get a controller in user-service to redirect to a movie-service endpoint, without hard coding the url?
#Controller
public class UserServiceController {
#PostMapping("/something")
public String performSomeAction() {
// I'd like movie service to be resolved to an instance that is registered with Eureka
return "redirect:http://movie-service/some-url
}
}
Is what I'm trying to do possible? I know Spring cloud is normally used with APIs, but to me it seems like it would make sense to be able to integrate your web applications with it as well.
#Controller
public class UserServiceController {
#Autowired
LoadBalancerClient loadBalancerClient;
#PostMapping("/something")
public String performSomeAction() {
ServiceInstance serviceInstance = loadBalancerClient.choose("movie-service");
if (serviceInstance != null) {
return "redirect:http://"+ serviceInstance.getHost() + ":" + serviceInstance.getPort() +"/some-url
}
// throw an error or something else
}
}
Related
I am working on a new Spring Boot reactive server application. The application exposes reactive endpoints as shown below:
#Configuration
public class XXXXRouter {
#Bean
public RouterFunction<ServerResponse> routes(ClaimsHandler handler) {
return nest(path("/api/xxxx"), nestedRoutes(handler));
}
private static RouterFunction<ServerResponse> nestedRoutes(xxxxHandler handler) {
return route(GET("/"), handler::allClaims)
.andRoute(GET("/{id}"), handler::byId)
.andRoute(GET("/{id}/notes"), handler::notesById);
}
}
I need to make these endpoints to swagger-ui like framework. I am using following dependencies to generate required swagger-ui and api-docs:
implementation 'org.springdoc:springdoc-openapi-webflux-core:1.2.32'
implementation 'org.springdoc:springdoc-openapi-webflux-ui:1.2.32'
I also have the following API config:
#Configuration
public class OpenAPIConfig {
#Bean
public OpenAPI customOpenAPI() {
return new OpenAPI().components(new Components()).info(new Info().title("Contact Application API")
.description("This is a sample Spring Boot RESTful service using springdoc-openapi and OpenAPI 3."));
}
}
localhost:8080/swagger-ui.html shows swagger-ui page. But, endpoints are not shown there.
Here is the output from localhost:8080/v3/api-docs:
{"openapi":"3.0.1","info":{"title":"Contact Application API","description":"This is a sample Spring Boot RESTful service using springdoc-openapi and OpenAPI 3."},"servers":[{"url":"http://localhost:8080","description":"Generated server url"}],"paths":{},"components":{}}
My question is how do we make the reactive endpoints available to swagger-ui.html?
I have a Spring boot application which uses Feign to call an external web service via Eureka. I'd like to be able to run the application using a mocked out implementation of the Feign interface, so I can run the application locally without necessarily having Eureka or the external web service running. I had imagined defining a run configuration that allowed me to do this, but am struggling to get this working. The issue is that the Spring "magic" is defining a bean for the Feign interface no matter what I try.
Feign interface
#FeignClient(name = "http://foo-service")
public interface FooResource {
#RequestMapping(value = "/doSomething", method = GET)
String getResponse();
}
Service
public class MyService {
private FooResource fooResource;
...
public void getFoo() {
String response = this.fooResource.getResponse();
...
}
}
I tried adding a configuration class that conditionally registered a bean if the Spring profile was "local", but that was never called when I ran the application with that Spring profile:
#Configuration
public class AppConfig {
#Bean
#ConditionalOnProperty(prefix = "spring.profile", name = "active", havingValue="local")
public FooResource fooResource() {
return new FooResource() {
#Override
public String getResponse() {
return "testing";
}
};
}
}
At the point my service runs, the FooResource member variable in MyService is of type
HardCodedTarget(type=FoorResource, url=http://foo-service)
according to IntelliJ. This is the type that is automatically generated by the Spring Cloud Netflix framework, and so tries to actually communicate with the remote service.
Is there a way I can conditionally override the implementation of the Feign interface depending on a configuration setting?
the solution is like below:
public interface FeignBase {
#RequestMapping(value = "/get", method = RequestMethod.POST, headers = "Accept=application/json")
Result get(#RequestBody Token common);
}
then define your env based interface:
#Profile("prod")
#FeignClient(name = "service.name")
public interface Feign1 extends FeignBase
{}
#Profile("!prod")
#FeignClient(name = "service.name", url = "your url")
public interface Feign2 extends FeignBase
{}
finally, in your service impl:
#Resource
private FeignBase feignBase;
Having posted the same question on the Spring Cloud Netflix github repository, a useful answer was to use the Spring #Profile annotation.
I created an alternative entry point class that was not annotated with #EnabledFeignClients, and created a new configuration class that defined implementations for my Feign interfaces. This now allows me to run my application locally without the need to have Eureka running, or any dependent services.
I'm using a simpler solution to avoid having multiples interfaces for a variable parameter like url.
#FeignClient(name = "service.name", url = "${app.feign.clients.url}")
public interface YourClient{}
application-{profile}.properties
app.feign.clients.url=http://localhost:9999
How can we use grcp communication with spring boot application. And how we can use common service discovery method for use with grpc to get end point of spring boot application.
For service discovery, implement your own NameResolver.
I used below code to get instance discovery client. It help to call all servers using revers poxing.
#Autowired
private DiscoveryClient discoveryClient;
and below method help to call all the services using service name
#RequestMapping(method = RequestMethod.GET, value = "/senduser")
public ResponseEntity<?> sendMessageToAllServices() {
user u=null;
List<ServiceInstance> server=discoveryClient.getInstances("grpc-server");
for (ServiceInstance serviceInstance : server) {
String hostName=serviceInstance.getHost();
int gRpcPort=Integer.parseInt(serviceInstance.getMetadata().get("grpc.port"));
ManagedChannel channel=ManagedChannelBuilder.forAddress(hostName,gRpcPort).usePlaintext(true).build();
UserServiceBlockingStub stub=UserServiceGrpc.newBlockingStub(channel);
UserDetail user=UserDetail.newBuilder()
.setName("Thamira")
.setEmail("Thamira1005#gmail.com")
.setAge(24).setGender(Gender.Male)
.setPassword("password").build();
u=stub.createUser(user);
}
return ResponseEntity.ok("User "+u);
}
we can change register as a consul or eureka. This method support both of that.
In Spring Boot 1.4.3 I exposed a SOAP web service endpoint which works successfully on port 8080.
For the purpose of running a health check I also need to expose a RESTful API. I tried both using Actuator and a rest controller:
#RestController
public class RESTapis {
#RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, value = "/health")
public String healthCheck() {
return "ACK";
}
}
but in both cases I get the same response: HTTP 405 (method not allowed).
The REST api returns HTTP 200 if I disable the web-service.
How can I have both the SOAP web-service and REST working at the same time?
Here is the web-service configuration:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/*");
}
}
So going off the messageDispatcherServlet method it looks like you are binding your servlet to all the incoming request due to the wildcard registration:
return new ServletRegistrationBean(servlet, "/*");
Hence the MessageDispatcher is intercepting all of your incoming requests and trying to find the /health and throwing http 405.
Fix:
return new ServletRegistrationBean(servlet, "/soap-api/*");
Explanation:
By binding the Message Dispatcher to a specific URI namespace we can ensure that all the request fired on the /soap-api/* namespace ONLY will be intercepted by the MessageDispatcher. And all the other requests will be intercepted by the DispatcherServlet making it possible to run a Rest interface in parallel to your Soap WS.
Suggestion:
Not knowing the reasons / specifics of the app, but going off the name of the method healthcheck(), you can look at using spring boot actuator to generate health checks, metrics for you app. You can also override it for customisations.
Reference for actuator: https://spring.io/guides/gs/actuator-service/
Any example how to integrate Spring Boot application with Spring Social Google (GabiAxel/spring-social-google) provider? I found this project, but it seems to be unfinished. Spring Boot explains how to get it working with Spring Facebook, Twitter, but is it the same for log in with Google?
As you have mentioned in your question, you can use that project hosted on github.
You can use this dependency
In a Configuration class, you will have to extend SocialConfigurerAdapter, override the addConnectionFactories method and add GoogleConnectionFactory. For example :
#Configuration
#EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
#Override
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
GoogleConnectionFactory googleConnectionFactory = new GoogleConnectionFactory(environment.getProperty("spring.social.google.app-id"), environment.getProperty("spring.social.google.app-secret"));
googleConnectionFactory.setScope("https://www.googleapis.com/auth/plus.login");
connectionFactoryConfigurer.addConnectionFactory(googleConnectionFactory);
}
#Bean
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public Google google(ConnectionRepository repository) {
Connection<Google> connection = repository.findPrimaryConnection(Google.class);
return connection != null ? connection.getApi() : null;
}
}
You can use this along with the Spring Social examples.