Getting straight to the point, I believe there is value to be had with a Header version of @ResponseStatus, being @ResponseHeaders.

// Basic Template, I could also see an @ResponseHeader sub annotation and an array of that.
@Target({ElementType.Method, ElementType.Type})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseHeaders {
      String[] headers() default {};
}

I'm personally a massive fan of avoiding the ResponseEntity<T> whenever possible, with exception being for an endpoint where the response status and headers are dynamic. Instead I prefer to use @RestController with the occasional @ResponseStatus and ResponseStatusException.

Recently I encountered the following,

@RestController
@RequestMapping("/rest/example")
public class ExampleController {

    @GetMapping
    public ResponseEntity<JsonNode> jsonNodeForTheExample() {
        return ResponseEntity.ok().cacheControl(CacheControl.maxAge(1, TimeUnit.DAYS).cachePrivate().mustRevalidate()).body(null);
    }
}

Now there are some ways to make this less painful, for example extracting the CacheControl instance into a static final field. However with something like a @ResponseHeaders, I could do the following.

@ResponseHeaders(
      headers= "Cache-Control=private max-age=86400 must-revalidate"
)
@Target({ElementType.Method, ElementType.Type})
@Retention(RetentionPolicy.RUNTIME)
public @interface ShortBrowserCache  {}

Then the controller reduces to the following.

@RestController
@RequestMapping("/rest/example")
public class ExampleController {

    @GetMapping
    @ShortBrowserCache
    public JsonNode jsonNodeForTheExample() {
        return null;
    }
}

I will concede however, for my specific example, the same can be achieved by the usage of a Filter and some annotation scanning, or static references to CacheControl instances. Beyond that there is also the same issue of resolution priority that @RequestMapping::produces and @ResponseStatus both have.

In my opinion a compelling reason for this from an annotation standpoint is that between @ResponseBody and @ResponseStatus, @ResponseHeaders is the last hurdle to mapping all the HTTP Pieces to a function without the function knowing it's an HTTP endpoint.

Comment From: bclozel

This duplicates https://github.com/spring-projects/spring-framework/issues/14179 - I don't think our position changed since and the argument is quite similar here.