I followed the sample from the Spring Cloud docu and have now configured in my application.yml
server:
port: 18081
servlet:
context-path: /myPath/services
zuul:
# don't add per default all services automatically to the Zuul server
ignoredServices: '*'
routes:
mytest:
#path: /checkPath/**
url: http://mytest.server.local/api/
and a configured Filter:
public class PreFilter extends ZuulFilter {
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 1;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println("Request Method : " + request.getMethod() + " Request URL : " + request.getRequestURL().toString());
return null;
}
}
When I test with
http://localhost:18081/myPath/services/mytest/test
I would expect that this works only if path is configured. If I leave it as above (uncommented) I get from ZuulPreFilter that it was hit. I wouldn't expect it since the path shouldn't be found. Is there a misunderstanding or do I miss something in my config? Why is the path hit at all because .../checkPath2/... won't be detected?
Related
I have a few micro-services powered by Ribbon, Eureka and OAuth2 auth server. Everything worked like a charm. Then I introduced Zuul API Gateway, and added a logger class to log my requests.
Zuul Gateway yml
spring:
application:
name: zuul-api-gateway-service
server:
port: 8765
eureka:
client:
service-url:
defaultZone: http://theusername:thepassword#localhost:8761/eureka
register-with-eureka: true
zuul:
sensitiveHeaders: Cookie,Set-Cookie, Authorization
routes:
job-service:
path: /api/**
url: http://localhost:7084
jobposting-service:
path: /api/**
url: http://localhost:6084
stripPrefix: false
oauth:
path: /auth/**
url: http://localhost:9191
Zuul Appplication class
#SpringBootApplication
#EnableZuulProxy
#EnableEurekaClient
public class ZuulApiGatewayServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApiGatewayServerApplication.class, args);
}
}
LoggingFilter
#Component
public class ZuulLoggingFilter extends ZuulFilter {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Override
public String filterType() {
return "pre"; //pre post error
}
#Override
public int filterOrder() {
return 1;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() throws ZuulException {
HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
logger.info("request -> {} request uri -> {}", request, request.getRequestURI());
//
// RequestContext ctx = RequestContext.getCurrentContext();
// // Add a custom header in the request
// ctx.addZuulRequestHeader("Authorization",
// request.getHeader("Authorization"));
// logger.info(String.format("%s request to %s", request.getMethod(),
// request.getRequestURL().toString()));
return null;
}
}
Then my API requests turned out not working. Any request, even with Authorization token attached to the header, returns 401 unauthorized. The logger not logging anything as well.
Thanks in advance
i have an external url and i want to pass some request header through zuul filter to launch the application.
Can anyone please help me on this.
In my custom prefilter i have written this:
#Component
public class CustomFilters extends ZuulFilter {
public static final Logger logger = LoggerFactory.getLogger(CustomFilters.class);
#Override
public String filterType() {
return "route";
}
#Override
public int filterOrder() {
return 1;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
logger.info("executing run ");
RequestContext ctx = RequestContext.getCurrentContext();
ctx.addZuulRequestHeader("x-forwarded-host", "<external url>");
ctx.addZuulRequestHeader("x-forwarded-proto", "https");
ctx.addZuulRequestHeader("x-forwarded-port", "8800");
ctx.addZuulRequestHeader("key", "id);
return null;
}
}
app.properties:
ribbon.eureka.enabled=false
server.port=8080
zuul.routes.books.sensitive-headers=
zuul.routes.books.path = /books/
zuul.routes.books.url = <ext url>
Sample Application:
This is giving me a rest url through which i am redirecting to external url defined above in my properties file.
public class SampleApplication {
#RequestMapping(value = "/check")
public String available() {
return "available!";
}
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}
app.properties:
spring.application.name=book
server.port=8090
Issue screenshot
zuul:
routes:
users:
path: /myusers/**
url: http://example.com/users_service
These simple url-routes do not get executed as a HystrixCommand, nor do they load-balance multiple URLs with Ribbon. To achieve those goals, you can specify a serviceId with a static list of servers, as follows:
zuul:
routes:
echo:
path: /myusers/**
serviceId: myusers-service
stripPrefix: true
hystrix:
command:
myusers-service:
execution:
isolation:
thread:
timeoutInMilliseconds: ...
myusers-service:
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
ListOfServers: http://example1.com,http://example2.com
ConnectTimeout: 1000
ReadTimeout: 3000
MaxTotalHttpConnections: 500
MaxConnectionsPerHost: 100
in your filter make sure that this filter should come into picture only for request with uri example1.com or example2.com by impelemting should filter method
How to make Zuul dynamic routing based on HTTP method (GET/POST/PUT...)?
For example, when you need to route the POST request to the different host instead of the default one described in 'zuul.routes.*'...
zuul:
routes:
first-service:
path: /first/**
serviceId: first-service
stripPrefix: false
second-service:
path: /second/**
serviceId: second-service
stripPrefix: false
I.e. when we request 'GET /first' then Zuul route the request to the 'first-service', but if we request 'POST /first' then Zuul route the request to the 'second-service'.
To implement dynamic routing based on HTTP method we can create a custom 'route' type ZuulFilter and resolve 'serviceId' through DiscoveryClient. Fore example:
#Component
public class PostFilter extends ZuulFilter {
private static final String REQUEST_PATH = "/first";
private static final String TARGET_SERVICE = "second-service";
private static final String HTTP_METHOD = "POST";
private final DiscoveryClient discoveryClient;
public PostOrdersFilter(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
#Override
public String filterType() {
return "route";
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter() {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String method = request.getMethod();
String requestURI = request.getRequestURI();
return HTTP_METHOD.equalsIgnoreCase(method) && requestURI.startsWith(REQUEST_PATH);
}
#Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
List<ServiceInstance> instances = discoveryClient.getInstances(TARGET_SERVICE);
try {
if (instances != null && instances.size() > 0) {
context.setRouteHost(instances.get(0).getUri().toURL());
} else {
throw new IllegalStateException("Target service instance not found!");
}
} catch (Exception e) {
throw new IllegalArgumentException("Couldn't get service URL!", e);
}
return null;
}
}
#Cepr0's solution is right. Here I am proposing just a simpler way (without service discovery). Assuming you have that route:
zuul:
routes:
first:
path: /first/**
# No need for service id or url
Then you can route requests for '/first' route in 'route' type filter just by setting location to request context.
#Component
public class RoutingFilter extends ZuulFilter {
#Override
public String filterType() {
return ROUTE_TYPE;
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() throws ZuulException {
/* Routing logic goes here */
URL location = getRightLocationForRequest();
ctx.setRouteHost(location);
return null;
}
}
How can i implement multiple zuulFallbackProvider for multiple zuul routing.
I cant see an answer how to do it using a properties only other than exposing a restcontroller and implement a method with a hystrixcommand.
Can i make each of my service with its own zuulFallBackProvider bean?
application.yml
server:
port: 8080
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 20000
ribbon:
ReadTimeout: 20000
ConnectTimeout: 20000
zuul:
prefix: /api
ignoredServices: '*'
host:
connect-timeout-millis: 20000
socket-timeout-millis: 20000
routes:
kicks-service:
path: /kicks/**
serviceId: kicks-service
stripPrefix: false
sensitiveHeaders:
kicks-inventory:
path: /inventory/**
serviceId: kicks-inventory
stripPrefix: false
sensitiveHeaders:
This is my sample app
#SpringBootApplication
#EnableDiscoveryClient
#EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
#Bean
public Prefilter prefilter(){
return new Prefilter();
}
#Bean
public ZuulFallbackProvider zuulFallbackProvider() {
return new ZuulFallbackProvider() {
#Override
public String getRoute() {
return "kicks-inventory";
}
#Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
#Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
#Override
public int getRawStatusCode() throws IOException {
return 200;
}
#Override
public String getStatusText() throws IOException {
return "OK";
}
#Override
public void close() {
}
#Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("fallback".getBytes());
}
#Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
};
}
}
Each route will need a separate bean. They should return different route for getRoute method.
Please check this- http://tachniki.blogspot.in/2017/02/generic-ZuulFallbackProvider.html. Hope it will make it slightly easier.
You can have a default fallback provider for all routes, otherwise you need a fallback provider per route.
If you would like to provide a default fallback for all routes than
you can create a bean of type FallbackProvider and have the
getRoute method return * or null.
http://cloud.spring.io/spring-cloud-netflix/multi/multi__router_and_filter_zuul.html#hystrix-fallbacks-for-routes
I have following applications:
Gateway-Service
Api-Service
Gateway-Serivce is Zuul proxy and Api-Service is basically rest + broker + websocket + other thechnologies.
I'm using sockjs + stomp to communicate with broker through zuul. For doing protocol upgrade socket handler need to have connection header set to Upgrade. When I request this on gateway this connection header is set to Upgrade but after the zuul processing header is set to Keep-Alive.
So first question is if it's ok to replace connection header with original request header and if it's ok then how to do that? Using some Zuul filter?
Thanks
for someone interested this could be the solution...all credits goes to #spencergibb
#Component
public static class WebSocketHeaderFilter extends ZuulFilter {
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
RequestWrapper wrapper = new RequestWrapper(ctx.getRequest());
String upgradeHeader = wrapper.getHeader("Upgrade");
if (null == upgradeHeader) {
upgradeHeader = wrapper.getHeader("upgrade");
}
if (null != upgradeHeader && "websocket".equalsIgnoreCase(upgradeHeader)) {
wrapper.addHeader("connection", "Upgrade");
ctx.addZuulRequestHeader("connection", "Upgrade");
ctx.setRequest(wrapper);
}
return null;
}
}