Problem
@Endpoint/@WebEndpoint operations that need to set a response header — Content-Disposition for a file download, Cache-Control for a page whose whole point is showing live status, etc. — currently have no way to do so. WebEndpointResponse only carries a body, status, and content type:
public WebEndpointResponse(@Nullable T body, int status, @Nullable MimeType contentType)
There's no headers equivalent anywhere in the operation model.
Why this is more pressing than it used to be
This was raised before in #22297 (2020) and declined, with the suggested workaround being @RestControllerEndpoint — full ResponseEntity control, including headers. That's exactly what we're relying on today for two endpoints, each blocked on a header WebEndpointResponse can't express:
- A file-export endpoint needs
Content-Disposition: attachment; filename="report.csv", so opening the URL in a browser downloads a sensibly-named file instead of rendering inline.
- A live status/dashboard HTML page needs
Cache-Control: no-store — a stale cached copy would silently show outdated status to whoever's relying on it.
But @RestControllerEndpoint/@ControllerEndpoint are now @Deprecated(forRemoval = true) (#31768), so building new endpoints on them means building on something explicitly discouraged for new code. Andy Wilkinson flagged the exact same gap in that thread:
I guess a missing piece is the ability to set a Content-Disposition header which WebEndpointResponse does not allow you to do at the moment.
— #31768 (comment)
#32823 is the same underlying need, scoped specifically to cache headers.
What we're asking for
A minimal, focused addition — not the broader functional-endpoint work tracked in #47313, and not complex-object binding for @WriteOperation — just a way for an operation to attach response headers. For example, an additional WebEndpointResponse constructor:
public WebEndpointResponse(@Nullable T body, int status, @Nullable MimeType contentType, HttpHeaders headers)
applied by both the WebMvc and WebFlux handler mappings, the same way status/contentType already are.
Context
Currently on Spring Boot 4.1. We'd rather not build new endpoints on a forRemoval = true annotation, and moving them to @Endpoint/@WebEndpoint today would mean silently losing headers our endpoints already depend on for correctness (not just convenience, in the Cache-Control case).
Problem
@Endpoint/@WebEndpointoperations that need to set a response header —Content-Dispositionfor a file download,Cache-Controlfor a page whose whole point is showing live status, etc. — currently have no way to do so.WebEndpointResponseonly carries a body, status, and content type:There's no
headersequivalent anywhere in the operation model.Why this is more pressing than it used to be
This was raised before in #22297 (2020) and declined, with the suggested workaround being
@RestControllerEndpoint— fullResponseEntitycontrol, including headers. That's exactly what we're relying on today for two endpoints, each blocked on a headerWebEndpointResponsecan't express:Content-Disposition: attachment; filename="report.csv", so opening the URL in a browser downloads a sensibly-named file instead of rendering inline.Cache-Control: no-store— a stale cached copy would silently show outdated status to whoever's relying on it.But
@RestControllerEndpoint/@ControllerEndpointare now@Deprecated(forRemoval = true)(#31768), so building new endpoints on them means building on something explicitly discouraged for new code. Andy Wilkinson flagged the exact same gap in that thread:#32823 is the same underlying need, scoped specifically to cache headers.
What we're asking for
A minimal, focused addition — not the broader functional-endpoint work tracked in #47313, and not complex-object binding for
@WriteOperation— just a way for an operation to attach response headers. For example, an additionalWebEndpointResponseconstructor:applied by both the WebMvc and WebFlux handler mappings, the same way
status/contentTypealready are.Context
Currently on Spring Boot 4.1. We'd rather not build new endpoints on a
forRemoval = trueannotation, and moving them to@Endpoint/@WebEndpointtoday would mean silently losing headers our endpoints already depend on for correctness (not just convenience, in theCache-Controlcase).