如何在 Spring Webflux 中获取当前控制器方法的 URL?

Joh*_*han 3 java spring-boot spring-webflux

这个问题类似于在 Spring MVC 中获取当前 URL 的最佳方法什么?但是对于 Webflux(它也可能与如何在 spring-webflux 中获取当前请求的上下文有关,但这个问题更具体,所以我希望有解决方案)。

我想要做的是在 Webflux 控制器中获取当前 URL。例如:

@GetMapping("/greeting")
public Mono<Greeting> greeting() {
   String linkToCurrentResource = .. // How do I get this?
   Greeting greeting = new Greeting("hello",  linkToCurrentResource);
   return Mono.just(greeting);
}
Run Code Online (Sandbox Code Playgroud)

如果在负载均衡器后面运行,最好linkToCurrentResource应该了解X-Forwarded-???标头。所以在这种情况下,我想linkToCurrentResource成为类似https://external-uri.com/api/greeting. 如果重要的话,我正在使用 Netty 服务器实现。

cac*_*co3 10

您可以将ServerHttpRequest用作方法参数来获取uri

@RestController
public class GreetingController {
  @GetMapping("/greeting")
  public Mono<Greeting> getGreeting(ServerHttpRequest serverHttpRequest) {
    return Mono.just(new Greeting("greeting", serverHttpRequest.getURI().toString()));
  }
}
Run Code Online (Sandbox Code Playgroud)

如果在负载均衡器后面运行,最好linkToCurrentResource应该了解X-Forwarded-???标头。

然后你可以公开一个.ForwardedHeaderTransformer @Bean

从它的文档:

从 " Forwarded" 和 " X-Forwarded-*" 标头中提取值以覆盖请求URI(即HttpRequest.getURI()),以便它反映客户端发起的协议和地址。

@Configuration
open class MvcConfig {
    @Bean
    open fun forwardedHeaderTransformer() = ForwardedHeaderTransformer()
}
Run Code Online (Sandbox Code Playgroud)

以下是一些测试:

@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)
    }
}
Run Code Online (Sandbox Code Playgroud)

Greeting.kt

data class Greeting(
        val g: String? = null,
        val uri: String? = null
)
Run Code Online (Sandbox Code Playgroud)