Define client target URL at runtime using Quarkus & Resteasy - java

I need to send HTTP requests from my Quarkus application. Following this guide, I have this RestClient:
#Path("/v1")
#RegisterRestClient
public interface CountriesService {
#GET
#Path("/name/{name}")
Set<Country> getByName(#PathParam String name);
}
In the Path annotation, I can configure the path. But the domain/url to call is defined in a configuration file, according to this paragraph.
# Your configuration properties
org.acme.rest.client.CountriesService/mp-rest/url=https://restcountries.eu/rest #
org.acme.rest.client.CountriesService/mp-rest/scope=javax.inject.Singleton #
In my case, I need this URL to be defined programmatically at runtime, as I receive it as a callback URL.
Is there a way to do that?

Quarkus Rest Client, and Quarkus Rest Client Reactive, implement the MicroProfile Rest specification and as such allow creating client stubs with RestClientBuilder programmatically, e.g.:
public class SomeService {
public Response doWorkAgainstApi(URI apiUri, ApiModel apiModel) {
RemoteApi remoteApi = RestClientBuilder.newBuilder()
.baseUri(apiUri)
.build(RemoteApi.class);
return remoteApi.execute(apiModel);
}
}
See https://download.eclipse.org/microprofile/microprofile-rest-client-2.0/microprofile-rest-client-spec-2.0.html#_sample_builder_usage
You cannot achieve this with client created with the #RegisterRestClient annotation

Related

Spring cloud gateway routing with path

I'm trying to write a gateway routing with a path (route everything to www.example.com/foobar/), but the '/foobar/' part is ignored, and everything is routed to www.example.com/)
My RouteLocator configuration is:
#Bean
public RouteLocator myRouteLocator(final RouteLocatorBuilder builder) {
return builder.routes()
.route(route -> route.path("/**").uri("http://www.example.com/foobar"))
.build();
}
When I call the service with http://localhost:8080/myApiCall, cloud gateway forwards the call to http://www.example.com/myApiCall instead of http://www.example.com/foobar/myApiCall.
If I call my service as http://localhost:8080/foobar/myApiCall, the resulting call is http://www.example.com/foobar/myApiCall, so it works correctly in this case.
Based on some debugging, my final URL is created here:
https://github.com/spring-cloud/spring-cloud-gateway/blob/v3.1.3/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java#L88
Where only the host is used, the path is omitted from the configuration.
My used versions:
spring-cloud-gateway: v3.1.3
spring-core: v5.3.20
I have thought about just using a rewritepath filter to always append the /foobar/ part - but isn't there a better way?

How to create a Spring Boot Actuator endpoint that delegates all calls to a different port?

I'm using Spring Boot 2.7.5 and I want to create an actuator endpoint, that acts as a proxy, and forwards all requests to a different server, running on the same JVM instance, but on a different port (say 8082). Here's the gist of it:
#Component
#RestControllerEndpoint(id = "myEndpoint", enableByDefault = true)
public class MyEndpoint {
#RequestMapping("**")
public Object myEndpoint() {
// TODO Forward everything to port 8082
return ...
}
}
What do I need to do in order to achieve this?
Update 1:
The port (8082) is not available from the internet so I can't do a simple redirect.
Update 2:
I don't want to forward the request to a Spring Controller or Spring Bean. Port 8082 is a separate server started in the same process.
What if instead of a redirect (as suggested in other replies) your actuator would perform a call to localhost:8082 and return it's return it's response? You could also return a ResponseEntity instead of just an Object to cascade the HTTP codes of the performed request.
#Component
#RestControllerEndpoint(id = "myEndpoint", enableByDefault = true)
public class MyEndpoint {
private static final String TARGET_HOST = "localhost";
private static final int TARGET_PORT = 8082;
#RequestMapping("**")
public Object myEndpoint(HttpServletRequest originalRequest) {
Uri targetUri = UriComponentsBuilder.fromHttpRequest(originalRequest)
.host(TARGET_HOST)
.port(TARGET_PORT)
.build();
Object responseBody = /* code to perfom the call using your preferred HTTP client*/;
return responseBody;
}
}
Spring Boot Actuator endpoint that delegates all calls to a different port.
For different port use this in your application.properties file
management.server.port=8081
management.endpoints.web.exposure.include=*
This will expose all actuator endpoints on port 8081.
The official document describes :
Base path for Web endpoints. Relative to the servlet context path (server.servlet.context-path) or WebFlux base path (spring.webflux.base-path) when the management server is sharing the main server port. Relative to the management server base path (management.server.base-path) when a separate management server port (management.server.port) is configured.
You can configure the following
management:
server:
port: 8081
servlet:
context-path:
endpoints:
web:
base-path: /
path-mapping:
prometheus: metrics
exposure:
include: [ "prometheus" ]
Tip : This configuration must be different with the server.ports must be different.
For more details, please check appendix.application-properties.actuator

Quarkus: request to URL given as string

I want to make an HTTP request with Quarkus. However, the target URL is not known at compilation time, it will be composed from different parts at runtime.
Quarkus provides a way to build static REST clients like this:
#Path("/v2")
#RegisterRestClient
public interface CountriesService {
#GET
#Path("/name/{name}")
#Produces("application/json")
Set<Country> getByName(#PathParam String name);
}
However, I am loking for something like the Python requests package:
url = 'https://stackoverflow.com/questions/ask'
response = requests.get(url)
I am building the application in Kotlin, so all Java and Kotlin libraries should work.
What should I use?
With the MP REST Client defined in an interface, you can use the programmatic client creation API:
CountriesService remoteApi = RestClientBuilder.newBuilder()
.baseUri("created at runtime url")
.build(CountriesService.class);
remoteApi.getByName("");

How to configure hot reload in Jhipster?

I am using Jhipster(Angular + Springboot) Application for my existing project.
I managed to create a controller(app.resource) manually apart from the ones already generated by jhiptser(using .jh file) for achieving a file download functionality.
So, when we start the server we usually initiate two servers i.e gradlew and npm start. The second runs on port 9000 which eventually supports hot reload functionality.(front-end development)
So the problem is, I am able to access those endpoints from the server running on standard 8000 port. However, from the port which is a proxy(9000), the method is returning 404.
I tried to clean build the application several times.
NOTE: The #RequestMapping value on the new controller is different then those present already.
Does this have to do something with spring security?
Thanks in advance.
Here is the previous controller:
#RestController
#RequestMapping("/api")
public class FGAppDiagramResource {
#GetMapping(value = "/fg-app-diagram-downloadFile")
public void getImage(String fileName,String folderName, HttpServletResponse
response){
// Some Code
}
}
Here is my New controller:
#RestController
#RequestMapping("/fileDownload")
public class DownloadFileController {
private final Logger log =
LoggerFactory.getLogger(DownloadFileController.class);
public DownloadFileController() {
super();
}
#Autowired
private ApplicationProperties applicationProperties;
#GetMapping(value = "/fg-app-diagram-downloadFile/{fileName}/{folderName}")
public void getImage(#PathVariable String fileName,#PathVariable String folderName, HttpServletResponse response) {
// Some Code
}
}
Your new controller does not use /api so you must add your endpoint URL /fileDownload to proxy configuration of webpack dev server in webpack/webpack.dev.js
proxy: [{
context: [
/* jhipster-needle-add-entity-to-webpack - JHipster will add entity api paths here */
'/api',
'/fileDownload',
You may want to use /api/fileDownload to avoid changing proxy configuration and also because /api is useful for many other aspects like security and also using HTML5 URL routing strategy in Angular to get rid of # in client routes (see https://github.com/jhipster/generator-jhipster/pull/9098).
/api and /management are namespaces to avoid route conflicts, so it is usually wise to use them for your new endpoints.

Consuming a RESTful WebService passing a JSON object as request body

I've defined a RESTful WebService (by using RESTEasy on JBoss AS 7) that consumes a JSON data stream.
#PUT
#Path("/send")
#Consumes(MediaType.APPLICATION_JSON)
public Response consumeJSON(Student student) {
String output = student.toString();
// Do something...
return Response.status(200).entity(output).build();
}
How can I call my WS from another Spring-based webapp, by properly using the RestTemplate, mapping a Java Object to JSON and passing it as request body?
Note: I'm asking about Spring with the aim to investigate the facilities provided by the framework. I well know that it is possible to do that by defining manually the request body.
Cheers, V.
In the client application, you can create an interface with the same signature as the one you expose on the server side, and the same path.
Then, in the spring configuration file, you can use the RESTeasy client API to generate a proxy connecting to the exposed webservice.
In the client application, it would look like this :
SimpleClient.java
#PUT
#Path("/send")
#Consumes(MediaType.APPLICATION_JSON)
public Response consumeJSON(Student student);
Config.java
#Bean
public SimpleClient getSimpleClient(){
Client client = ClientFactory.newClient();
WebTarget target = client.target("http://example.com/base/uri");
ResteasyWebTarget rtarget = (ResteasyWebTarget)target;
SimpleClient simple = rtarget.proxy(SimpleClient.class);
return simple;
}
Then, in the place where you want to invoke this web service, you inject it with Spring and you can call the method. RESTeasy will search for the webservice matching with with your client (according to the path and the request type) and will create a connection.
Launcher.java
#Resource
private SimpleClient simpleClient;
public void sendMessage(Student student) {
simpleClient.consumeJSON(student);
}
Docs on the RESTesay client API : http://docs.jboss.org/resteasy/docs/3.0.7.Final/userguide/html/RESTEasy_Client_Framework.html
Hope this was helpfull.

Categories

Resources