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)?
Related
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.
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 got a Spring boot REST based proxy server application with standard REST End point exposed(GET, POST,PUT and DELETE) to outside world.
Any requests coming from external world then will be routed to actual functionality internally.
Now the requirement is to expose all the API supported by my REST proxy server. As I mentioned I do not have static controller path in my code for individual APIs. So would like to get your input how to create swagger support for all the internal APIs that my proxy server support. Is there are a way I can generate swagger schema manually to mimics the controller path? Or is there any other way to solve this problem?
Thanks in advance.
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.
I have a Zuul reverse proxy in front of my spring boot microservices, used as an API gateway.
Can the Zuul spring boot app be used to deliver static content, IE client code that calls the API gateway? I dont want to host this in a microservices or have another VM to manage for hosting content
I tried having a static folder in resources folder, but cant figure out how to map this in the application.yml responsible for routing. I dont want to route it to a NGnix or apache server, but use the embedded Tomcat of Zuul Spring boot app.
What would be the route to put in the route config, or what is the best approach for this.
Thank you
You don't have to map it in the routes config. Whatever you put in the resources/static folder will be served, as long nothing else is mapped on the same path.