How to get URL of the current controller method in Spring Webflux?

Johan :

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.

caco3 :

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 understand X-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 request URI (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
)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=117708&siteId=1