Swagger UI try It! does not work with Kubernetes ingress - spring-boot

We have a Java Spring Boot project with Swagger and docker. We deploy it on kubernetes behind an ingress controller.
It works properly in localhost (using postman and swagger-ui try button).
Problem comes when we deploy it.
Rest Controller:
#ApiOperation(value = "Operation",
notes = "it does something<br />")
#RequestMapping(value="/operation", method=RequestMethod.POST)
#ApiResponses({
#ApiResponse(code = 200, message = "OK")
})
#ResponseBody public ResponseEntity<String> operation(#RequestBody BodyThing thing) {
return new ResponseEntity<>("OK", HttpStatus.OK);
} //operation
Now the ingress:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapp-ingress
namespace: __NAMESPACE__
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- hosts:
- test.host.com
secretName: key-pair
rules:
- host: test.host.com
http:
paths:
- path: /myapp
backend:
serviceName: myapp-service
servicePort: 8080
Then, with the app online deployed on K8S, usign an app like postman we must call:
https://test.host.com/myapp/operation in order to call the API. It works OK.
The problem comes if we enter in Swagger UI portal: https://test.host.com/myapp/swagger-ui.html
If we try the API call inside the swagger UI, it tries to call to https://test.host.com/operation and it fails with 404 code.
Swagger-UI makes the Endpoint URL with: host + basepath + operation_path and that is: test.host.com + / + operation
It doesnt aggregate ingress path.
How can we deal with it?
Of course is something that only happends if we deploy it with the ingress controller because, we add the /myapp path.
Thanks!

The problem is how to get swagger's base path to match to the one that is being used behind the proxy. There is more than one solution as per https://github.com/springfox/springfox/issues/1443
Since you have a specific host, I'd suggest going with the one to change the base path that swagger knows about based upon the host the request goes to. This way you can set it differently for localhost vs in the remote host. You'd need to set a RelativePathProvider for your host in your custom docket section of your Swagger #Configuration class like in https://github.com/springfox/springfox/issues/1443#issuecomment-274540681

Related

SpringOpenApi Swagger UI not found in Kubernetes, but work very much fine in local

Technical Stack,
Springboot 2.6.7,
java 8 or 11
Springopenapi 1.6.9
Swagger properties
springdoc:
api-docs:
enabled: true
path: /api-docs
swagger-ui:
enabled: true
display-Request-duration: true
path: /swagger-ui-myname.html
use-root-path: true
filter: true
supportedSubmitMethods: nomethod**
#Bean
public OpenAPI myOpenApi()
{
return new OpenAPI().info( new Info().title( "XXXXX" )
.description( "XXXXX" ) );
}
#Bean
public GroupedOpenApi myExternalOpenApi()
{
String[] paths = { "XXXX" };
return GroupedOpenApi.builder()
.group( "XXXXXX" )
.displayName( "XXXXXXX" )
.pathsToMatch( paths )
.build();
}
is my swagger configuration.
this works perfectly in my local machine, I can access http://localhost:8091/api-docs or http://localhost:8091/swagger-ui-myname.html(this redirects to http://localhost:8091/swagger-ui/index.html), and I can see my group api completely all is well till here.
But when I deploy this on a pod in kubernetes as a service, and try to see swagger ui on https://server:port/service-name/swagger-ui-myname.html or http://server:port/service-name/swagger-ui/index.html, these two requests landed in 404 not found.
Note: I am able to access http://server:port/service-name/api-docs, I can see the complete json of my group api documentation.
Can you please give me clue what went wrong in Kubernetes deployment. I have tried all the options from github ofspring openapi bug logs/comments or from stackoverflow.
One more adding to this, on a springboot app start , can we log the swagger ui html absolute pathe, where it reside to access or, add the url in /api-docs or related url.
I think the issue you are facing is described and answered here: https://github.com/springdoc/springdoc-openapi/issues/153
Solution should be setting the forward-headers-strategy in your application.yml to framework:
server:
forward-headers-strategy: framework
And also in your kubernetes ingress configurations be sure the X-Forwarded-Prefix header is set to /service-name
For example, if you are using an nginx ingress it should contain:
metadata:
annotations:
nginx.ingress.kubernetes.io/x-forwarded-prefix: "/service-name"

Spring Gateway builds incorrect URL with query string parameters

I have a Spring gateway microservices with this mapping:
- id: advertisementService4
uri: lb://advertisementService
predicates:
- Path=/public/breeds/**
filters:
- RewritePath=/public/breeds/(?<segment>/?.*), /v1/public/breeds/$\{segment}
If I make a call with Postman to this url: http://192.168.99.1:8050/public/breeds/500, the gateWay service solves the mapping and build the new url in the correct way (to /v1/public/breeds/500):
But if I call to this url http://192.168.99.1:8050/public/breeds?petType=Dog, the gateWay service chooses the correct mapping, but it builds the url in a incorrect way:
The gateWay service builds http://7a32a826ec7a:8070/public/breeds?petType=Dog instead of http://7a32a826ec7a:8070/v1/public/breeds?petType=Dog (with v1 in the URL)
I don't understand why. Could you help me please?
I fixed it changing the RewritePath:
from RewritePath=/public/breeds/(?/?.), /v1/public/breeds/${segment}
to RewritePath=/(?/?.), /v1/${segment}

spring clould oauth2 redirect to "/" after login with spring cloud gateway

I'm a new comer for the spring oauth2. With the document check, I do a simple function for the oauth2 in a spring cloud alibaba nacos environment.
In the design, I want to add redirect for oauth request from gateway to auth micro service. Then auth micro service authorize with form login, and authorize the scope, then redirect to the redirect url set in oauth url.
When raising request to auth micro service directly, it works well. But if raising request to gateway, after log in, it will redirect to "/" direction and raise 404 error.
With investigation for SavedRequestAwareAuthenticationSuccessHandler, I found the session contained in request in null. Further more, I check the HttpSessionRequestCache, it also gets null session when saving the request. Since the request is not saved, it can't redirect to origin url after login and redirect to default url "/".
But I don't know why the request related session is empty. I also try to do a demo project like following:
#RequestMapping(value = "hello",method = RequestMethod.GET)
public RedirectView hello(HttpServletRequest request){
System.out.println("Hello:"+request.getSession().getId());
return new RedirectView("thanks");
}
#RequestMapping(value = "thanks",method = RequestMethod.GET)
public ResponseEntity<String> thanks(HttpServletRequest request){
System.out.println("Thanks:"+request.getSession().getId());
return new ResponseEntity("aaa",HttpStatus.OK);
}
The printed session id are different when using gateway. But without gateway, they are same. The following is my gateway settings:
server:
port: 8080
spring:
application:
name: flower-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true
routes:
- id: order-routes
uri: lb://flower-order
predicates:
- Path=/order/**
- id: core-routes
uri: lb://flower-core
predicates:
- Path=/**
- id: auth-routes
uri: lb://flower-auth
predicates:
- Path=/oauth/**
So I guess that this is caused by spring cloud gateway. But I don't know how to fix it. Please give me some information. Thanks.
New updates:
As I change the auth gateway to
- id: auth-routes
uri: https://127.0.0.1:8082
predicates:
- Path=/oauth/**
it passes normal behavior. So does it means that it is caused by load balance. But as I just have one running entity, not quite sure. Does this mean it should use the distribute session management such as spring session? As in actual production environment, the auth service won't only have one entity.

Spring Cloud Gateway to modify HREF static HTML

I have a Spring Gateway and a Webflux service behind it. The route is;
server:
port: 9999
spring:
application:
name: discovery-service
cloud:
gateway:
routes:
- id: route1
predicates:
- Path=/1/**
uri: http://localhost:8081
filters:
- RewritePath=/1/(?<myPath>.*), /$\{myPath}
If I request localhost:9999/1/index.html, it translates to localhost:8081/index.html and the page is correctly returned.
However, in the HTML I have links and references, for example
test1
How would I get the Gateway to return the HTML with corrected HREF? (i.e. return the HTML as);
test1
On a side note; is it common or accepted practice to host static HTML, CSS and JS in the Webflux/Netty server containing the microservices, or should they be placed somewhere else?
You just need to remove the first / in your href
test1
Or add a .
test1

Spring Cloud: default redirecting from Gateway to UI

I'm new to microservices and Spring Boot. I have a few Spring Cloud microservices with a Zuul gateway running on port 8080.
browser
|
|
gateway (:8080)
/ \
/ \
/ \
resource UI (:8090)
There is a UI microservice on port 8090, which has a controller with a method inside, returning index.html.
I configured Zuul routing like this for UI (I'm using Eureka too):
zuul:
routes:
ui:
path: /ui/**
serviceId: myproject.service.ui
stripPrefix: false
sensitiveHeaders:
If I call http://localhost:8080/ui/ everything works fine and I see rendering of my index.html.
Is it possible to configure Spring Cloud in some way to make the following flow work: calling http://localhost:8080/ redirects us to controller of UI microservice, which returns index.html?
So the idea is to open UI from the root of my site.
Thanks in advance!
Finally, I've made my code work! Thanks to #pan for mentioning Zuul Routing on Root Path question and #RzvRazvan for revealing about how Zuul routing works.
I've just added controller to Zuul routed Gateway microservice with one endpoint to redirect from root http://localhost:8080/ to http://localhost:8080/ui/:
#Controller
public class GateController {
#GetMapping(value = "/")
public String redirect() {
return "forward:/ui/";
}
}
Zuul properties for redirecting from Gateway microservice on port 8080 as http://localhost:8080/ui/ to UI microservice, which implemented as a separate Spring Boot application on port 8090 as http://localhost:8090/ui/:
zuul:
routes:
ui:
path: /ui/**
serviceId: myproject.service.ui
stripPrefix: false
sensitiveHeaders:
UI microservice's properties:
server:
port: 8090
servlet:
contextPath: /ui
Eventually, this call http://localhost:8080/ redirects us to controller of UI microservice, which returns view index.html:
#Controller
public class UiController {
#GetMapping(value = "/")
public String index() {
return "index.html";
}
}
Actually, I had another problem with rendering static content in such architecture, but it was connected with configuration of my front-end, which I develop using Vue.js framework. I will describe it here in a few sentences, in case it might be helpful for someone.
I have the following folders structure of UI microservice:
myproject.service.ui
└───src/main/resources
└───public
|───static
| ├───css
| └───js
└───index.html
All content of public folder is generated by npm run build task from webpack and vue.js. First time, I called my http://localhost:8080/ I got 200 OK for index.html and 404 for all other static resources, because they was called like this:
http:\\localhost:8080\static\js\some.js
So it was configured wrong public path for static assets in webpack. I changed it in config\index.js:
module.exports = {
...
build: {
...
assetsPublicPath: '/ui/',
...
}
...
}
And static assets became to be called properly. e.g.:
http:\\localhost:8080\ui\static\js\some.js
If you would like to have on Zuul the UI(front-end) you can add the static content in resources/static folder(html, css and js files). On this way your proxy is able to render the index.html (of course you must have an index.html in static folder). O this way on http://localhost:8080 the proxy will render index.html; also you can have another paths but all these path are managed by index.html)
About routing, the Zuul only redirect the http request. http://localhost:8080/ui/. On 8080 is running the proxy (Zuul) BUT /ui is the context path of resource server. Se when you make a call on this path http://localhost:8080/ui/ the proxy will redirect to resource server and will actually make a request to http://localhost:8090/ui/
It is a difference between browser path and http request path. The browser path is managed by index.html and the http request is managed by Zuul. I don't know if I was explicit enough.
One more thing... You can have the same path (/ui) on http request and index.html and when your browser will access the http://localhost:8080/ui/ a .js file with http request method will make an http request to http://localhost:8080/ui/ and then will be redirected to http://localhost:8090/ui/ and the response from the resource server will be rendered on the page from http://localhost:8080/ui/.

Resources