I want to achieve the following with a spring boot webflux application:
I have an endpoint api/test. I would like the same controller to be available on dynamically configured sub paths. E.g. if configured a sub-route "app" then a request to app/api/test should end up in the same controller.
To facilitate this I did the following using a RouteLocatorBuilder:
route(id = "proxy_api_test") {
host(location.host)
path("/${location.route}/api/test/**" )
filters{
filter(setPathGatewayFilter.apply(createSetPathConfig("/api/test")))
}
uri(location.uri)
}
In case of testing on localhost for example location.host would be "localhost:8080" and location.route could be "app" and location.uri would be "http://localhost:8080".
And createSetPathConfig is given as:
fun createSetPathConfig(template: String): SetPathGatewayFilterFactory.Config{
val config = SetPathGatewayFilterFactory.Config()
config.template = template
return config
}
When running the application that would work like a charm because requests to http://localhost:8080/app/api/test would be redirected to http://localhost:8080/api/test with the help of spring cloud gateway. I have chose this approach also because it could be various sub paths at the same time, so the same controller must be available from different entry paths.
Now what I see is that this does not work in unit-tests using an #Autowired val client: WebTestClient because when executing the unit-test in fact no web-server is running. Is there a way to indicate with the uri in with RouteLocatorBuilder that the request should be executed on the same host such that unit-test would also work with the same logic? Because in fact in this case I would like spring cloud gateway not to forward to another host but to just change the routes dynamically.
Related
How do I make Spring Boot Zuul Proxy Server handle a specific request locally, instead of proxying it to some other Server?
Let's say I want to do a custom health check of Zuul Proxy Server itself through an API which should return a local response, instead of returning a proxied response from some other remote server.
What kind of route configuration is required to forward the request?
Following is what I figured out and it worked for me:
1) Create a local request handler:
Add a regular Controller/RestController with a RequestMapping in a file in Zuul proxy Server code
#RestController
public class LocalRequestHandler {
#GetMapping(path = "/status", produces = MediaType.TEXT_PLAIN_VALUE);
private static ResponseEntity<String> getServiceStatus() {
String status = "I am alive";
return ResponseEntity.ok().body(status);
}
}
2) Create a local forwarding route in the configuration file (application.properties or as the case may be) where other routes are defined. In my case, Spring Boot Zuul Proxy server is running on Jetty container with GatewaySvc as the application context.
zuul.routes.gatewaysvc.path=/GatewaySvc/status
zuul.routes.gatewaysvc.url=forward:/GatewaySvc/status
For a standalone Spring Boot, which I have not tested, you may have to remove the context path from the above configuration.
zuul.routes.gatewaysvc.path=/status
zuul.routes.gatewaysvc.url=forward:/status
Reference:
Strangulation Patterns and Local Forwards
I have two Java processes - which get spawned from the same Jar using different run configurations
Process A - Client UI component , Developed Using Spring bean xml based approach. No Spring Boot is there.
Process B - A new Springboot Based component , hosts REST End points.
Now from Process A , on various button click how can I call the REST end points on Process B using Feign Client.
Note - Since Process A is Spring XML based , right at the moment we can not convert that to Spring boot. Hence #EnableFeignClients can not be used to initialise the Feign Clients
So Two questions
1) If the above is possible how to do it ?
2) Till Process A is moved to Spring boot - is Feign still an easier option than spring REST template ?
Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSockets and you can easily use feign without spring boot. And Yes, feign still better option to use because Feign Simplify the HTTP API Clients using declarative way as Spring REST does.
1) Define http methods and endpoints in interface.
#Headers({"Content-Type: application/json"})
public interface NotificationClient {
#RequestLine("POST")
String notify(URI uri, #HeaderMap Map<String, Object> headers, NotificationBody body);
}
2) Create Feign client using Feign.builder() method.
Feign.builder()
.encoder(new JacksonEncoder())
.decoder(customDecoder())
.target(Target.EmptyTarget.create(NotificationClient.class));
There are various decoders available in feign to simplify your tasks.
You are able to just initialise Feign in any code (without spring) just like in the readme example:
public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
...
}
Please take a look at the getting started guide: feign on github
I have a service which calls the REST Api and that REST API call is chargeable, so i want when we deploy the code in Dev & QA environment, mock response should be returned while actual functionality testing.
How can i achieve that ?
Mock Rest API response in actual code flow
The endpoint for your api shouldn't be hard-coded in the code, instead you can set the endpoint in a properties file.
ie. In a file like .../myapp/src/resources/application.properties
some.service.api.endpoint=someservice.com/api
Then in your code you can use Spring's #Value annotation to get the value.
#Service
public class SomeServiceApi {
#Value("some.service.api.endpoint")
private String endpoint;
// ...
While in development, the property can point to some mock endpoint that you've set up yourself, or if the api supports it, a sandbox endpoint for the api.
I'm new to Jhipster, and wondering if it's possible to manually call a microservices from the gateway code using a RestTemplate or something else.
My first idea was to call the gateway itself... but I'm not sure it's a good idea.
My second idea was to try and call the service by it's URL. My concern is that I don't want to hardcode the port of a given node. Instead, i want to use proper loadbalancing.
I've read this article https://dzone.com/articles/spring-cloud-rest-client-with-netflix-ribbon-basic, but the injection failed.
I've read somewhere else that you now need to manually add the bean declaration
#LoadBalanced
#Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
But now I'm struggling with the actual URI : what I am supposed to put as the root? (xxxxx)
final HcpVersionedhcp hcpVersionedhcp =
restTemplate.exchange("http://xxxxx/api/user-data/byLogin/", UserData.class);
The only configuration I have in my gateway application.yml is
ribbon:
eureka:
enabled: true
The "xxxxx" has to be replaced with your services name. If your service is "foo", you should write http://foo/api/user....
If you are using JWT as authentication, you need to auth using a user a in JHipster, or to pass the JWT token from request when possible. However the is no best practice for JWT auth, so I would suggest to go the JHipster UAA way.
In a few words, you have one more service responsible for authentication and authorization. To access your service from another service, you can use #AuthorizedFeignClient on interfaces, similar to JPA.
So you define:
#AuthorizedFeignClient(name = "xxxx")
interface XxxClient {
#RequestMapping(value = "/api/some-entities/{id}")
SomeEntity getSomeEntityById(Long #Path("id") id);
}
And inject it in any spring service / rest-controller like this:
#Inject
private XxxClient xxxClient;
//...
public void someAction() {
//...
xxxClient.getEntityById(id);
//..
}
Which internally implement client authorization flows ...
Spring Boot: I want to have achieved the following: some URL paths are mapped to a port, some to another.
In other words I'd like something like:
public class Controller1 {
#RequestMapping(value="/path1", port="8080") public...
#RequestMapping(value="/path2", port="8081") public...
}
So that my app responds to both localhost:8080/path1 and localhost:8081/path2
It's acceptable to have 2 separate controllers within the app.
I have managed to partially succeed by implementing an EmbeddedServletContainerCustomizer for tomcat, but it would be nice to be able to achieve this inside the controller if possible.
Is it possible?
What you are trying to do would imply that the application is listening on multiple ports. This would in turn mean that you start multiple tomcat, since spring-boot packages one container started on a single port.
What you can do
You can launch the same application twice, using different spring profiles. Each profile would configure a different port.
2 properties:
application-one.properties: server.port=8080
application-two.properties: server.port=8081
2 controllers
#Profile("one")
public class Controller1 {
#RequestMapping(value="/path1") public...
}
#Profile("two")
public class Controller2 {
#RequestMapping(value="/path2") public...
}
Each controller is activated when the specified spring profile is provided.
Launch twice
$ java -jar -Dspring.profiles.active=one YourApp.jar
$ java -jar -Dspring.profiles.active=two YourApp.jar
While you cannot prevent making call on the undesired port, you can specify HttpServletRequest among other parameters of the method of the controller, and then use HttpServletRequest.getLocalPort() to obtain the port the call is made on.
Then you can manually return the HTTP error code if the request is made on the wrong port, or forward to another controller if the design is such that same path on different ports must be differently processed.