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?
Related
I'm trying to build a simple api-gateway using spring-cloud-gateway. So far I understood the basic principle but I'm running in a specific Problem:
The target url which I'm forwarding my request potentially contains zero, one or multiple path segments. Unfortunately, these path segments are ignored.
private final String routingTargetWithPath = "http://hapi.fhir.org/baseR4";
#Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("patient", r -> r
.path("/Patient", "/Patient/*")
.and()
.method(GET)
.uri(routingTargetWithPath)
)
.build();
}
Using curl sending the request to my api-gateway:
curl http://localhost:8080/Patient
and accordingly
curl http://localhost:8080/Patient/2069748
I would assume the requests would be routed to:
http://hapi.fhir.org/baseR4/Patient
and accordingly
http://hapi.fhir.org/baseR4/Patient/2069748
But instead they are being routed to:
http://hapi.fhir.org/Patient
and accordingly
http://hapi.fhir.org/Patient/2069748
So, the path of the configured routing url is ignored.
Unfortunately, I can't do a manual rewrite here, since in production the "routingTarget" will be configured and I don't know, if and how many path segments it will contain.
How can I achieve to route to the complete configured routing target?
Ok, I found the answer:
According to here it is intentional, that the path of the uri is ignored.
So in my case, the set path filter would fix the problem:
private final URI routingTargetWithPath = URI.create("http://hapi.fhir.org/baseR4");
#Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("patient", r -> r
.path("/Patient", "/Patient/*")
.and()
.method(GET)
.filters(f -> f.prefixPath(routingTargetWithPath.getPath()))
.uri(routingTargetWithPath)
)
.build();
}
I have a spring cloud gateway application and what I want is like if there are two routes then on one route it should redirect to some external application but for other route it should forward the request to same app with a particular url.
-id: my_local_route
predicates:
- Path="/services/local"
uri: "/mylocal/services/local" //can we do something like that
Please note I want to create my rest services in same app as in spring cloud gateway. I understand it is not correct approach but for my knowledge I wanted to know whether it is possible or not.
If you have some rest APIs within your spring-cloud-gateway project, you don't need to explicitly put the routes for it.
So suppose you have following rest api in gateway project
#RestController
#RequestMapping("test")
class Controller{
#GetMapping("hello")
public String hello(){
return "hello";
}
}
and for external-url, you want to send some traffic to let's say https://httpbin.org. So in gateway application.yml could look something like this:
spring:
cloud:
gateway:
routes:
- id: httpbin-route
uri: https://httpbin.org
predicates:
- Path=/status/**
With this request like
http://localhost:8080/test/hello will be resolved by your rest controller
http://localhost:8080/status/200 will be redirected to httpbin site
If for some reason you have the same root path for both cases, the controller will have precedence.
If you have the same endpoint in gateway predicates and controller, by default controller will take precedence over predicates, if you want predicates to take precedence over controller, just create a BeanPostProcessor to adjust the order:
#Component
public class RequestMappingHandlerMappingBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerMapping) {
((RequestMappingHandlerMapping) bean).setOrder(2); // After RoutePredicateHandlerMapping
}
return bean;
}
}
I define a filter in my spring gateway(2.2.8.RELEASE) project like this:
#Component
public class LogFilter2 extends AbstractGatewayFilterFactory {
#Override
public GatewayFilter apply(Object config) {
return (exchange,chain) -> {
System.out.println("LogFilter2 flitered!!!");
return chain.filter(exchange);
};
}
}
then config the filter in application.properties like this:
# dolphin music
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.routes[0].id=dolphin-music-service
# forward by ip:port way
spring.cloud.gateway.routes[0].uri=http://10.107.64.246:11014
# forward by service name way
# spring.cloud.gateway.routes[0].uri=lb://
spring.cloud.gateway.routes[0].predicates[0]=Path=/music/**
spring.cloud.gateway.routes[0].filters[0]=LogFilter2
but when I run the project and send a request to the url /music/xxxxxx, the request did not enter the filter.No log LogFilter2 flitered!!! output.what should I do to make the filter works as expect? I also tried many other way,this is the minimal demo:https://github.com/jiangxiaoqiang/java-learn. In this demo, I define different kind of gateway filter, no one work except the global gateway filter. I am struggle with this problem for days.
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
This question already has answers here:
Will CORS policy prevent resource access from non-browser requests?
(4 answers)
Why isn't my CORS configuration causing the server to filter incoming requests? How can I make the server only accept requests from a specific origin?
(1 answer)
Closed 3 years ago.
I have two projects A and B. Both run on different ports A(8080) B(8091). I had enabled cors origin(http://localhost:8090) in project A for endpoint "greeting". Then trying to call "greeting" endpoint from project B using resttemplate. Since cross origin in project A and the calling Project B are from different ports, I am expecting a cors related error.
But I am getting a proper response instead of an error.
I tried in chrome and postman but same result
//Project A
#RestController
public class GreetingController {
#GetMapping("/greeting")
#CrossOrigin(origins = "https://localhost:8090")
public String greeting() {
return "greetingsss";
}
}
//Project B
#RestController
public class GreetingController {
#GetMapping("/greeting1")
public String greeting() {
final String uri = "http://localhost:8080/greeting";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
return result;
}
}
Expected Result: failed to load, no access control allow origin
Actual Result: greetingsss
CORS is a web thing imposed by user agents(browsers). Hence, service to service communication won't be affected by that. If you want to achieve that for service to service communication, use spring security where you can intercept URLs by matching certain patterns and check a variety of stuff including origin IP/domain (hasIpAddress). Here, you may deny access or allow access using various rules.
Alternatively, you may write a security filter to disallow/allow certain IP/domains.