What is @ResponseStatus and how do you use it to control the HTTP status code returned from a Spring MVC controller?
answer
- Annotation sets HTTP status
- On method → success status; on exception → error status
- code/value = HttpStatus; optional reason
- Static, fixed per type
- ResponseStatusExceptionResolver applies it
basics
~10 s@ResponseStatus is a Spring annotation that sets the HTTP status code of a response. You put it on a custom exception class or a controller method, e.g. @ResponseStatus(HttpStatus.NOT_FOUND) on a NotFoundException.
solid answer
~40 s@ResponseStatus is a Spring MVC annotation that maps a handler outcome to a specific HTTP status. Two common placements: (1) on a controller method, so the successful response uses that status instead of the default 200, e.g. @ResponseStatus(HttpStatus.CREATED) on a POST handler; (2) on a custom exception class, so whenever that exception propagates out of a handler, Spring's ResponseStatusExceptionResolver catches it and sets the declared status. Its key attribute is code (aliased as value), an HttpStatus enum; you can also pass a reason string. It is declarative and static: the status is fixed per method or exception type and cannot vary on runtime data. When you need a runtime-decided status, you throw a ResponseStatusException instead.
code
java · 21 lines@ResponseStatus(HttpStatus.NOT_FOUND)
public class ProductNotFoundException extends RuntimeException {
public ProductNotFoundException(String id) {
super("No product with id " + id);
}
}
@RestController
class ProductController {
@GetMapping("/products/{id}")
public ProductDto get(@PathVariable String id) {
return repo.find(id)
.orElseThrow(() -> new ProductNotFoundException(id)); // → 404
}
@PostMapping("/products")
@ResponseStatus(HttpStatus.CREATED) // → 201 on success
public ProductDto create(@RequestBody NewProduct body) {
return service.create(body);
}
}go deeper
Know it sets the status code and can go on an exception or a method.
Explain the static nature and that a resolver applies it for exceptions.
Contrast with ResponseStatusException and mention the resolver chain.
Discuss when annotating exceptions scales vs. becomes an anti-pattern (too many exception classes).
## What it is `@ResponseStatus` is a Spring Web MVC annotation (`org.springframework.web.bind.annotation.ResponseStatus`) that declares which HTTP status code the response should carry. "Declarative" means you state the intent with an annotation instead of writing imperative code that mutates the response. ## Two placements **1. On a controller method.** By default a returning handler produces HTTP 200 (OK). Annotating the method overrides that default for the success path: ```java @PostMapping("/users") @ResponseStatus(HttpStatus.CREATED) // 201 instead of 200 public UserDto create(@RequestBody NewUser body) { ... } ``` This is handled at method-invocation time by `ServletInvocableHandlerMethod`, which reads the annotation and applies the status before writing the body. **2. On an exception class.** When a handler throws an exception, Spring's exception-resolver chain inspects the exception. If the exception type carries `@ResponseStatus`, the `ResponseStatusExceptionResolver` sets that status: ```java @ResponseStatus(HttpStatus.NOT_FOUND) public class ProductNotFoundException extends RuntimeException { } ``` Throw `new ProductNotFoundException()` anywhere in the call stack of a controller method and the client gets 404. ## Attributes - `code` (and its alias `value`) — an `HttpStatus` enum constant (e.g. `HttpStatus.NOT_FOUND`). - `reason` — an optional String; when present it changes how the response is written (via `sendError`, covered in the reason-mapping question). ## Key property: it is static Because the value lives in an annotation, the status is baked in per method or per exception type. You cannot decide "404 vs 409" at runtime from one annotated exception. That limitation is exactly why `ResponseStatusException` (a programmatic alternative) exists. ## Gotchas - On a method, the annotated status wins even if you also try to return a `ResponseEntity` with a different status — avoid mixing them; prefer one mechanism. - If you catch the exception inside the controller and return normally, `@ResponseStatus` never fires — it only triggers when the exception actually propagates out of the handler. - The annotation is read from the raised exception type (and its meta-annotations), not from a wrapped cause. ## When to use Use `@ResponseStatus` on exceptions for a small, fixed set of domain error types where each maps cleanly to one status. Use it on methods to express non-200 success codes like 201/202/204 concisely.
- Where does @ResponseStatus on an exception actually get processed?By the ResponseStatusExceptionResolver, one of the default HandlerExceptionResolver beans in the DispatcherServlet chain. It reads the annotation from the thrown exception type and applies the status.
- If you both annotate a method with @ResponseStatus(CREATED) and return a ResponseEntity with 200, what happens?Mixing is a bug; the method-level @ResponseStatus is applied, but the two mechanisms conflict and behavior is confusing. Pick one — use ResponseEntity when you need to set status dynamically, @ResponseStatus for a fixed success code.