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.