Issues resolving image @{} while using both context path and spring cloud gateway as reverse proxy
antechrestos opened this issue · 2 comments
Runtime context
- springboot version 2.5.5
- spring cloud 2020.0.4
- Thymeleaf 2.0.12
Context
I have two application: the first one is a spring cloud gateway running (let's say locally to make it easier it) with a simple discovery
server:
port: 8080
spring:
cloud:
gateway:
discovery:
locator:
url-expression: "uri"
predicates:
- name: Path
args:
patterns:
- "'/' + serviceId + '/**'"
filters:
- name: RewritePath
args:
regexp: "'/' + serviceId + '/?(?<remaining>.*)'"
replacement: "'/${remaining}'"
discovery:
client:
simple:
instances:
gui:
- uri: http://localhost:${aas.local.ports.admin}
metadata:
management.context-path: /manage
And the proxified spring boot named gui with following configuration
server:
port: 8081
forward-headers-strategy: "FRAMEWORK"
With this configuration (without context path for gui application), any use of @{/js/resource.js}
for example, will work like charm when gui application is accessed through spring gateway application.
For instance, provided that /home
endpoint of gui application serve a template that has th:src=@{/js/my-js.js}
, when accessing
http://localhost:8080/gui/home, the computed url of js in served page will be http://localhost:8080/gui/js/my-js.js.
Also, while working directly on gui application, I can access to my endpoint through http://localhost:8081/home and it will resolve js address to http://localhost:8081/js/my-js.js
So far so good.
However, when I put a context path on my gui application, things are a little bit different.
I set then server.servlet.context-path: /my/context/path
on gui application and while navigating to http://localhost:8081/gui/my/context/path/home, it serves me the computed template with computed url pointing to http://localhost:8080/gui/js/my-js.js instead of http://localhost:8080/gui/my/context/path/js/my-js.js .
Accessing gui directly (http://localhost:8081/my/context/path/home) does resolve js with context path to http://localhost:8081/my/context/path/js/my-js.js
Anything that I am missing?
Thank you
After investigation, seems like spring filter is eating the context path ... Enjoy your meal 😄
For anyone encountering this behaviour
public class ForceContextPathFilter extends OncePerRequestFilter {
public ForceContextPathFilter(String contextPath) {
this.contextPath = contextPath;
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return false; // filter path you don't want to impact
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
if (contextPathIsSet() && !contextPath.equals(request.getContextPath())) {
UriComponents uriComponents = UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request)).build();
final String scheme = uriComponents.getScheme();
final String host = uriComponents.getHost();
final String port = uriComponents.getPort();
final String path = new UrlPathHelper().getPathWithinApplication(request);
HttpServletRequestWrapper wrappedRequest = new HttpServletRequestWrapper(request) {
@Override
public String getContextPath() {
return super.getContextPath() + ForceContextPathFilter.this.contextPath;
}
@Override
public String getRequestURI() {
return this.getContextPath()+path;
}
@Override
public StringBuffer getRequestURL() {
return new StringBuffer()
.append(scheme)
.append("://")
.append(host)
.append(":")
.append(port)
.append(this.getContextPath())
.append(path);
}
};
chain.doFilter(wrappedRequest, response);
} else {
chain.doFilter(request, response);
}
}
private boolean contextPathIsSet() {
return !"".equals(contextPath) && !"/".equals(contextPath);
}
}