I've been using webflux in Spring Boot 3.2.x and I've implemented a custom actuator endpoint like this:

@Component
@RestControllerEndpoint(id = "api", enableByDefault = true)
class ApiProxyEndpoint {

    @Value("\${management.endpoints.web.base-path}")
    lateinit var managementPath: String

    @Autowired
    lateinit var webHandler: WebHandler

    @RequestMapping("**") // Match all path segments under <management-path>/api/ until the end of the path
    fun apiProxyEndpoint(exchange: ServerWebExchange): Mono<Void> {
        val originalPath = exchange.request.path.toString()
        val apiPathWithoutManagementPath = originalPath.substringAfter(managementPath)
        val updatedRequest = exchange.request.mutate().contextPath("/").path(apiPathWithoutManagementPath).header("X-Api-RootPath" managementPath).build()
        val updatedExchange = exchange.mutate().request(updatedRequest).build()

        return webHandler.handle(updatedExchange)
    }
}

The reason for doing this is that I want to expose the hal-browser as an actuator endpoint (so that I can browse the API from using the hal-browser from the actuator). What's happening is that when the hal-browser calls a URI (by calling the "api" endpoint in actuator) I redirect all requests to the actual api endpoint (not behind actuator). This endpoint acts as a proxy, just forwarding all requests made to "api" to somewhere else. This has worked great for several years, but since Spring Boot 3.3.0, the RestControllerEndpoint has been deprecated and I can't find a way to replace it. I've tried this:

@Component
@WebEndpoint(id = "api", enableByDefault = true)
class ApiProxyEndpoint {

    @Value("\${management.endpoints.web.base-path}")
    lateinit var managementPath: String

    @Autowired
    lateinit var webHandler: WebHandler

    @WriteOperation
    @ReadOperation
    @DeleteOperation
    fun apiProxyEndpoint(exchange: ServerWebExchange): Mono<Void> {
        val originalPath = exchange.request.path.toString()
        val apiPathWithoutManagementPath = originalPath.substringAfter(managementPath)
        val updatedRequest = exchange.request.mutate().contextPath("/").path(apiPathWithoutManagementPath).header("X-Api-RootPath" managementPath).build()
        val updatedExchange = exchange.mutate().request(updatedRequest).build()

        return webHandler.handle(updatedExchange)
    }
}

but this doesn't work. The example above will show me this error in the HAL browser:

{
  "timestamp": "2024-06-12T10:23:17.366+00:00",
  "status": 400,
  "error": "Bad Request",
  "requestId": "efc7c7b9-4",
  "message": "Missing parameters: exchange"
}

How can I achieve the same thing (i.e. proxying) in Spring Boot 3.3.0 without using the deprecated RestControllerEndpoint?

Comment From: wilkinsona

Could you please comment on https://github.com/spring-projects/spring-boot/issues/31768 rather than using a separate issue for this. It'll help to keep things in one place.

Comment From: johanhaleby

@wilkinsona Of course, I searched in the issues before posting this one but I couldn't find anything similar. So thanks for the pointer and sorry for the separate issue.