Feature request:
Sometimes I get the requirement to return a response with custom header with static value, x-custom-header: abc
on specific REST endpoints. I am aware of other ways to achieve this, e.g. with ResponseEntity
or custom filter, but I was wondering if we could have a feature similar to @ResponseStatus
where we could do it in annotation-driven way. Reason is that I'd like to keep code explicit, easy to read and minimal, which kind of rules out mentioned alternatives.
I'd expect the usage of this annotation to mostly mirror the usage of @ResponseStatus
, so these should be supported:
@RestController
@ResponseHeader(key = "x-custom-header", value = "abc")
public class Example {
// every REST method in this class will contain this header in response
}
@PostMapping("/example")
@ResponseStatus(HttpStatus.CREATED)
@ResponseHeader(key = "x-custom-header", value = "abc")
public Example postExample() {
return new Example();
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseHeader(key = "x-custom-header", value = "abc")
public class BadRequestException extends RuntimeException {
}
Also, I am aware of this related issue, but I am not sure I follow the reasoning for closing it.
Comment From: rstoyanchev
10968 was a proposal to have @ResponseHeader
on a Map
or HttpHeaders
method parameter, as an output container to which the controller method could add response headers. It was declined because there are alternatives like injecting HttpEntity
, returning `ResponseEntity, or directly to the response.
You're proposing a method annotation, which is a little different. What I'm wondering is how useful would a static header value be? Also does the header really only apply to one controller or others as well? An @ControllerAdvice
that implements ResponseBodyAdvice
, might be a better fit, allowing re-use, dynamic values, and more cross-cutting. Have you considered that?
Comment From: StefanRankovic
Hi @rstoyanchev, thanks for the feedback. As for the first part, you're right, the #10968 is quite different to what I'm proposing. As for the second part, I had a look at ResponseBodyAdvice
, and while I'm quite certain that it could do the trick, it would still require too much coding to achieve what, seems to me, is a fairly simple thing.
Also does the header really only apply to one controller or others as well?
This is the tricky part, in our use cases it usually only applies to a specific method, sometimes even dependent on the response status. Here is one example that might illustrate things better: - Significant portion of our mobile users have disabled the automatic app updates - As such, the outdated apps can only partially comprehend the updated resources they get from backend, and are missing out on the new stuff - We don't want to force users to upgrade the app, unless they hit the specific endpoints, in which case the app should suggest upgrade to users (minimal supported version for the endpoint is delivered via custom header, e.g. x-min-app-version: 1.1.0, note that header value is static) - This header might be e.g. set on POST endpoint of a resource, but not on GET, or it might only be set when specific 4xx exception is thrown, it really depends on case-by-case basis
To be clear, right now we're using a OncePerRequestFilter
to set this header on specific endpoints, so it's not that there is no way to achieve this, it's just that @ResponseHeader
on method/class/exception as described in the OP would be a welcome convenience. Also, our product is running on a microservice architecture, so having less things to type and configure per microservice is always better.
Comment From: bclozel
The edge cases being described above actually make a case against the annotation with static values: depending on the HTTP method, the response status... I think the ResponseBodyAdvice
contract is a better choice here as the behavior can be thoroughly tested independently. The static header assumes that all controller endpoints must then be independently tested for this case.
If a more targeted selection of endpoints is required, adding a custom @MinAppVersion
annotation on the controller methods could also be a way for the ResponseBodyAdvice
to know whether the controller handler should be considered (see ResponseBodyAdvice#supports
).
I also agree with Rossen on the fact that this is likely to create an overlap with existing contracts and ask questions when both ResponseEntity
and @ResponseHeader
are used on the same controller method, for the same header. Overwrite the header entirely? Add a value to the header?
To summarize, I don't think we should add this contract as the benefit is limited and it might not fit well with the existing support.
Comment From: rstoyanchev
I agree, there are also benefits to having this defined in a single, global place for testability and readability. I can also imagine that @ResponseHeader
can be overused. If multiple headers are added per controller method, and that is repeated in different controllers, that should be done in code instead.