Hi, let's say that we have a simple architecture with ingress gateway in front that handles all incoming requests, and orders service that is responsible for handling incoming requests about orders domain.
When such request is send to the ingress gateway: GET /api/orders, it should be rerouted to different path, let's say GET /orders. In this case, when orders service returns an error, for example 400 Bad Request, it would contain error details, like:
{
"timestamp":"2023-09-07T09:29:59.044+00:00",
"path":"/orders",
"status": 400,
"error":"Bad Request",
"requestId":"31520c96/1-8460"
}
If we take a closer look at path property, it contains wrong path from caller perspective. I wonder if we should add an option, similar to server.error.include-message config property to exclude path from error attributes.
What do you think?
Comment From: wilkinsona
The option to not include the message is for security purposes. That doesn't apply so much to the path. If the gateway set the X-Forwarded-Prefix header and the app was configured to consume it, I think the path in the error response would be correct. Have you got that set up?
If you really want to remove the path attribute, you could provide a custom ErrorAttributes implementation that excludes it. Such an implementation could extend DefaultErrorAttributes and remove the path attribute from the map returned from getErrorAttributes.
Comment From: AleksanderBrzozowski
@wilkinsona Thanks for response π
I've just generated a new project with webflux, and tried such a test:
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
properties = [
"server.port=8080",
"server.forward-headers-strategy=native"
]
)
class HeaderTestApplicationTests {
private val client = WebTestClient
.bindToServer()
.baseUrl("http://localhost:8080")
.build()
@Test
fun `should use X-Forwarded-Prefix path in error`() {
val response = client.get()
.uri("/bar")
.header("X-Forwarded-Prefix", "/foo")
.exchange()
response.expectBody()
.jsonPath("path")
.isEqualTo("/foo")
}
}
Unfortunately, it fails, path is equal to /bar. Is it something wrong with the test, or it isn't supported by netty? π
Comment From: quaff
kotlin server.forward-headers-strategy=native
Have you tried with server.forward-headers-strategy=framework ?
Comment From: AleksanderBrzozowski
Yup, I did, it is the same:
@Test
fun `should use X-Forwarded-Prefix path in error`() {
assertEquals(serverProps.forwardHeadersStrategy, FRAMEWORK)
val response = client.get()
.uri("/bar")
.header("X-Forwarded-Prefix", "/foo")
.exchange()
response.expectBody()
.jsonPath("path")
.isEqualTo("/foo") // reports failure, because it is equal to /bar
}
Comment From: wilkinsona
To my surprise, ServerRequest uses requestPath().pathWithinApplication().value() which means that the prefix is ignored. That can be changed with custom error attributes:
@Bean
ErrorAttributes customErrorAttributes() {
return new DefaultErrorAttributes() {
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
Map<String, Object> attributes = super.getErrorAttributes(request, options);
attributes.put("path", request.requestPath().value());
return attributes;
}
};
}
In your test, this produces a path of /foo/bar.
I'd like to discuss this with the team as it feels like this would be better default behaviour. However, I may be overlooking something.
Comment From: AleksanderBrzozowski
Oh, yeah, you are right, the path should be /foo/bar π
Thanks for your suggestion, we created a similar custom error attributes bean to handle this case π
Comment From: philwebb
We discussed this today on our team call and we're going to look at what happens with the Servlet version so that we can align. We also think adding an exclude option is worthwhile.
Comment From: mhalbritter
I was playing around with that on the Servlet stack. My findings:
- the
nativestrategy ignoresX-Forwarded-Prefixcompletely, both for errors and for normal dispatches. Tested with Tomcat. - the
frameworkstrategy works for normal dispatches,request.getRequestURI()returns the path with theX-Forwarded-Prefixprepended - the
frameworkstrategy doesn't work for error dispatches. TheDefaultErrorAttributesuses thejakarta.servlet.error.request_uriattribute, which doesn't takeX-Forwarded-Prefixinto account.
Here's the project: sb-37269.zip
Comment From: mhalbritter
I've created a ticket for the path inclusion configuration: https://github.com/spring-projects/spring-boot/issues/38619
Comment From: wilkinsona
the framework strategy doesn't work for error dispatches. The
DefaultErrorAttributesuses thejakarta.servlet.error.request_uriattribute, which doesn't takeX-Forwarded-Prefixinto account.
I think this has been addressed by https://github.com/spring-projects/spring-framework/issues/30828
Comment From: mhalbritter
Thanks for the pointer. Just tested with 6.1.2-SNAPSHOT, this resolves the problem when using server.forward-headers-strategy=framework on the servlet stack.
Comment From: mhalbritter
Now that this is fixed on the servlet stack, I think we should change our org.springframework.boot.web.reactive.error.DefaultErrorAttributes to use
errorAttributes.put("path", request.requestPath().value());
WDYT?
Comment From: wilkinsona
Makes sense to me. I'm not 100% sure on the timing of the change though. It feels like a bug so 3.1.x is tempting but I wonder if there's a chance it'll break someone in which case 3.3 would be better with the option to provide a custom DefaultErrorAttributes extension that uses the old path. I'd prefer 3.1.x if we think it's reasonable.
Comment From: wilkinsona
We discussed this today and decided to address this in 3.3 as the change may not be 100% backwards compatible. In the meantime, those that want the proposed behavior today can use a custom ErrorAttributes bean as shown above.