Due to a certain lack of support between two technologies I wanted to implement, I find myself in the need to look for a "smarter" proxy server than the current Apache solution I implemented. After some quick digging I found out about Spring Cloud Gateway and Zuul. As I wanted to keep things simple with dependencies, I choose Spring's option.
After some tries in my local environment with an API I had for a different purpose, I managed to get the routing right and a static page when the root is hit. Then I rolled it to my test environment and after dealing with some slightly annoying things I didn't know about Spring Cloud, the proxy worked with another API I have in that environment. I must add, this test were done in curl.
The problem I have now is that the Gateway is supposed to be used to proxy to other, non-boot application deployed in Tomcat. The routing itself works as stated in the title, but nothing is displayed. I double checked the servers were up and access to them properly directly from my browser. Then I tried to hit the test API, also deployed in a Tomcat, with my browser and found that the Get request worked ok but the Post request return 405 error (No error is logged in the prompt).
I've been looking around about this but haven't found anything yet. My initial, uneducated guess was that Spring Cloud Gateway was ment/designed to work with API based applications and/or the front end framework we use is not supported somehow, but I highly doubt that is the case.
As you see, I'm diving into this a bit blindly but the examples I've seen so far aren't particulary deep either, probably because it is fairly simple to understand.
My Java code is nothing but the Bean necesary for the Static content. I moved from Java config to yaml config as this gateway will need to be implemented in two servers with different apps on them. I'll share it anyway for the sake of completeness.
Java code:
#SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
#Bean
public RouterFunction<ServerResponse> statucResourceLocator(#Value("classpath:/static/index.html") Resource index) {
return route(GET("/"), request -> ok().contentType(MediaType.TEXT_HTML).bodyValue(index));
}
}
application.yml
server:
port: 8080
spring:
cloud:
gateway:
routes:
# Non-boot app
- id: app1
uri: localhost:8081
predicates:
- Path=/app1/**
# Non-boot app
- id: app2 <- Non-boot app
uri: localhost:8082
predicates:
- Path=/app2/**
- id: api
uri: http://localhost:8000
predicates:
- Path=/api/**
Is there something I missed from the Spring tutorial on this? Or is there a certain filter I need to add for other type of content to be displayed? We are using ZK Framework, Java 8 and our Spring version for the current projects is 4.3.8. Yes, we have a bit too much work done to make drastic updates or to change the backend.
Quick update:
I guess this was obvious, but the behaviour is the same if I test this on my local machine with an app that is not an API. I'll try to find the limitations Spring Gateway currently has, as it's still pretty new technology as far as I know.
Related
I am a little out of my league on this one as I am still getting familiar with everything Springboot. Onto my problem...
I am unable to access actuator for an application that is running in a fat jar file on an application server. All works great when I run the application locally through Eclipse as I am perfectly able to access a couple of the endpoints (health, logfile) via a browser and Postman.
However, when I attempt to access those same endpoints (via curl, a browser or Postman) using the application server's url, I get a 404. I am able to access other custom written apis within the application with no issue, just not actuator apis.
I know I am missing something very obvious, but cannot figure out what that is.
Good - http://localhost:9091/actuator/health --> from a browser or Postman
Not good - http://my-app-testserver-01:9090/actuator/health or curl localhost:9090/actuator/healthand both yield the below error. NOTE that the curl is performed on the application server.
"timestamp":"2022-06-30T20:57:12.191+00:00","status":404,"error":"Not Found","path":"/actuator/health"
What else? Oh yeah, below is a snippet from my yml file pertaining to actuator and I believe that is ok.
Any insight on this is greatly appreciated. Thank you.
management:
server:
port: 9090
endpoints:
web:
exposure:
include: "health,info,logfile" ```
This is all set now. I was setting up the application on a new server and had to have the ports opened up for me. Once that was done I was able to access the Actuator apis with no issues.
I'm having this problem with a new Spring Boot service, written in Kotlin, where I start to get 404 for any REST requests after some random time (When I deploy the service, instead, everything works fine, with the same REST APIs).
In addition, I've also other services, written in Java, that are running without any problem.
Just to specify:
all the components are well defined in the application;
I've moved logging filters and some authorization logic in libraries also used in the java services (services that are running perfectly);
I am not able to reproduce locally, it happens after some time only in staging and prod envs.
Then I've noticed a really strange thing:
when running a public request (so that I skip all the auth logic checks) the logger prints the response status as 200 -> but then the final answer that the client gets is 404.
Do you have any idea or tip?
Thanks
Problem fixed! It was a mistake in the middleware, there were two service without the correct routing setup -> traefik was not able to redirect the traffic in the right way
As a continuation of my learning of the Spring Cloud Gateway, I have created a small microservice (from Spring Boot) that serves web pages. This microservice is simple enough: it uses an application.properties file that contains the following:
server.port=8091
spring.resources.static-locations=classpath:/public/
The microservice has an index page in the resources folder, along with some style and other resources:
resources/public/index.html
resources/public/css/styling.css
resources/public/graphics/<pretty graphics files>
The Spring Cloud Gateway application uses a fairly straightforward routing configuration:
#Configuration
public class RouteConfig
{
#Bean
public RouteLocator gatewayRoutes(RouteLocatorBuilder routeLocatorBuilder)
{
return routeLocatorBuilder.routes()
.route("webdisplay",route-> route.path("/webdisplay/**").uri("localhost:8091"))
.build();
}
}
The Spring Cloud Gateway application listens on Port 8080.
When I access the simple microservice diectly:
http://localhost:8091
The index.html page is displayed as normal. Unfortunately, when I access the microservice through the Gateway:
http://localhost:8080/webdisplay
I get a blank page!
The page is blank, and the page source has nothing )(no HTML, nothing!). When using my browser's network debugger, I am seeing a response code of 200 with headers showing that HTML and other related web content is accepted. The HTML, however, is not actually getting to the browser.
Assuming that I am missing something, I went looking for examples of how to get HTML content through a Spring Cloud Gateway application. There are plenty of examples of how to get lists and text from toy microservices, and examples of how to serve static pages from a Spring Cloud Gateway application, but it appears that no one has used Spring Cloud Gateway to access webpages served by other applications! at least, no one has put up an example of doing so.
So if I am missing something that needs to be done, I haven't found it. Or perhaps this is a bug in the Gateway that needs to be reported somewhere?
Can anyone help with this? Does anyone know how to get Spring Cloud Gateway to work correctly with microservices that serve actual web content (static or dynamic)?
I am a noob in spring boot. I am writing a Gateway for some services. In a condition, I need to forward user request to other services (some Restful API) after authentication. I have done some search on 'forward' and 'redirect'. I think I need 'forward'. But I still have some questions: 1. when I forward it to other URI(eg. abc.dce.com/service/), does the service get the request body. 2.How can I do it in spring boot? Do you guys have a good example that fit my condition? (I admit that I am kind of lazy for this, but there are really many style of forward that confused me.)
//I find this example, but this is forwarding to service in same package //under same Internet.
#Override
public void addViewControllers(ViewControllerRegistry registry) {
// forward requests to /admin and /user to their index.html
registry.addViewController("/portal").setViewName(
"forward:/app/index.html");
}
Since you mention you're new to spring boot, you might want to take a look at spring project that implements a fully-featured gateway. I've used an earlier version of it (zuul) and the current spring-cloud-gateway allows you to implement a complete gateway easily by creating a spring-boot project and configuring. It has a lot of features you'll likely want to implement as a gateway (like adding/removing headers, modifying payloads,..). If you need features they don't support, you can implement via filters and other interfaces they provide. This was initially opensourced from Netflix so it is fairly comprehensive.
https://spring.io/projects/spring-cloud-gateway
Sample project:
https://github.com/spring-cloud-samples/spring-cloud-gateway-sample
I have a simple Zuul app that has a single route in the application.yml to route to my microservice. It's working.
However, what I'm looking for is a more dynamic solution where I can wire up routes dynamically, either through code or perhaps by POSTing to some Zuul endpoints during a build (possibly by using springfox and a swagger definition from microservices). I could not find an API for Zuul.
I'm somewhat aware of Eureka and that seems like a solution to abstract away the routing by doing discovery. However, I'm curious if there's a solution without introducing Eureka. If there's a way to wire up these routes in Zuul during a build vs. having to edit the application.yml every time.
Thanks in advance.
If you go for Eureka this will actually work ootb. Zuul as packaged in spring cloud will automatically expose every service using its name. So if you register a service called users in Eureka, Zuul will automatically create a route /users forwarding to the instances by default. That will only allow simple url structures but should solve your problem.
Please see the official documentation for details:
By convention, a service with the ID "users", will receive requests from the proxy located at /users (with the prefix stripped). The proxy uses Ribbon to locate an instance to forward to via discovery, and all requests are executed in a hystrix command, …
I'm actually editing a blog post about this exact topic (Routing and Filtering using Spring Cloud Zuul Server) but the source code has been available and working for some time now. Feel free to use it as a reference:
https://bitbucket.org/asimio/zuulserver
https://bitbucket.org/asimio/discoveryserver (in case routes are configured with serviceIds)
https://bitbucket.org/asimio/demo-config-properties/src (Zuul-Server-refreshable.yml where routes are dynamically updated).
Look at the refreshable Spring profile settings. This Zuul setup works with both, hard-coding routes url or discovered using Eureka.
It also acting as a Spring Cloud Config client so that routes could be dynamically updated via Git, which is also covered in another blog post: Refreshable Configuration using Spring Cloud Config Server, Spring Cloud Bus, RabbitMQ and Git.