This question is similar to What's the best way to get the current URL in Spring MVC? but for Webflux (it might also be related to How to get the context of the current request in spring-webflux but this question is more specific so I'm hoping there's a solution).
What I want to do is to get a hold of the current URL in a Webflux controller. For example:
@GetMapping("/greeting")
public Mono<Greeting> greeting() {
String linkToCurrentResource = .. // How do I get this?
Greeting greeting = new Greeting("hello", linkToCurrentResource);
return Mono.just(greeting);
}
Preferably linkToCurrentResource
should understand X-Forwarded-???
headers if running behind a load balancer. So in this case the I'd like linkToCurrentResource
to be something like https://external-uri.com/api/greeting
. I'm using the Netty server implementation if that matters.
You can use the ServerHttpRequest
as a method parameter to get the uri
:
@RestController
public class GreetingController {
@GetMapping("/greeting")
public Mono<Greeting> getGreeting(ServerHttpRequest serverHttpRequest) {
return Mono.just(new Greeting("greeting", serverHttpRequest.getURI().toString()));
}
}
Preferably
linkToCurrentResource
should understandX-Forwarded-???
headers if running behind a load balancer.
Then you can expose a ForwardedHeaderTransformer
@Bean
.
From its documentation:
Extract values from "
Forwarded
" and "X-Forwarded-*
" headers to override the requestURI
(i.e.HttpRequest.getURI()
) so it reflects the client-originated protocol and address.
@Configuration
open class MvcConfig {
@Bean
open fun forwardedHeaderTransformer() = ForwardedHeaderTransformer()
}
Here are some tests:
@ExtendWith(SpringExtension::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
properties = ["server.port=4333"])
class GreetingController2Test {
@Autowired
private lateinit var restTemplate: TestRestTemplate
@Test
fun `should return uri`() {
val responseEntity = restTemplate.getForEntity("/greeting", Greeting::class.java)
val greeting = responseEntity.body!!
assertEquals("http://localhost:4333/greeting", greeting.uri)
}
@Test
fun `should return uri composed from forwarded-??? headers`() {
val headers = HttpHeaders()
headers["X-Forwarded-Host"] = "external-uri.com"
headers["X-Forwarded-Proto"] = "https"
headers["X-Forwarded-Prefix"] = "/prefix"
val httpEntity = HttpEntity(null, headers)
val responseEntity = restTemplate.exchange("/greeting", HttpMethod.GET, httpEntity, Greeting::class.java)
val greeting = responseEntity.body!!
assertEquals("https://external-uri.com/prefix/greeting", greeting.uri)
}
}
And the Greeting.kt
:
data class Greeting(
val g: String? = null,
val uri: String? = null
)