Skip to content

Support custom HTTP response headers on @Endpoint/@WebEndpoint operations #51134

Description

@dmfrey

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    for: team-meetingAn issue we'd like to discuss as a team to make progressstatus: waiting-for-triageAn issue we've not yet triaged

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions