@ResponseStatus
Sets the HTTP status for a handler method's successful response, or for an exception class whenever it is thrown.
@ResponseStatus(HttpStatus.CREATED)- Added in Spring Boot Spring Framework 3.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$valueoptional | HttpStatus= HttpStatus.INTERNAL_SERVER_ERROR | Alias for code. |
$codeoptional | HttpStatus= HttpStatus.INTERNAL_SERVER_ERROR | The status to send. |
$reasonoptional | String= "" | If set, Spring calls HttpServletResponse.sendError with this text; the method's return value is not written and the body comes from the error page. Avoid on REST endpoints. |
Return value
n/aOn a handler method: the status used when the method returns normally. On an exception class: the status used when that exception (or a subclass) escapes a handler and no @ExceptionHandler deals with it.
@ResponseStatus declares a status code instead of building a ResponseEntity. It has two jobs, depending on where you put it.
On a handler method
It sets the status for a normal return. The common uses are CREATED on create endpoints, NO_CONTENT on void delete methods and ACCEPTED on endpoints that queue work. The status is fixed: if the method needs to choose between 200 and 201, or add a Location header, return ResponseEntity instead.
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
Tag create(@RequestBody NewTag body) { ... }
@ResponseStatus(HttpStatus.NOT_FOUND)
class TagNotFoundException extends RuntimeException { ... }On an exception class
When an annotated exception escapes a controller and no @ExceptionHandler claims it, Spring sends that status. The body comes from Spring Boot's error handling, so its shape depends on your configuration, and the exception message is only included if you allow it. That makes this a quick way to get correct status codes, but a weak way to give clients useful detail. For full control, handle the exception in a @RestControllerAdvice, or extend ErrorResponseException so the exception carries its own problem detail for a ResponseEntityExceptionHandler to render.
The annotation also ties a domain exception to HTTP. That is acceptable in a small app; in a layered one, keep domain exceptions free of web annotations and map them in one advice class.
The reason trap
Setting reason makes Spring call response.sendError(status, reason). The servlet container then renders an error response, and on a handler method your return value is thrown away. On a 201 that means the created object never reaches the client. Leave reason empty on REST code.
Alternatives
ResponseStatusException: throw it with a status and message for one-off cases, no new class needed.ErrorResponseExceptionsubclasses: carry a full RFC 9457 problem detail with type, title and extra properties.ResponseEntity: when headers or the status depend on runtime values.
Example
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
record Tag(long id, String label) {}
record NewTag(String label) {}
(HttpStatus.NOT_FOUND)
class TagNotFoundException extends RuntimeException {
TagNotFoundException(long id) {
super("Tag " + id + " not found");
}
}
("/api/tags")
public class TagController {
(HttpStatus.CREATED) // success status for this method
Tag create( NewTag body) {
return new Tag(7, body.label());
}
("/{id}")
Tag one( long id) {
throw new TagNotFoundException(id); // becomes 404
}
("/{id}")
(HttpStatus.NO_CONTENT)
void delete( long id) {
}
}
When to use it
- Returning 201 from a create endpoint without building a ResponseEntity
- Making a void delete method answer 204 No Content
- Giving a simple not-found exception a 404 in a small service
Frequently asked questions
Which wins, @ResponseStatus on the method or a ResponseEntity status?
The ResponseEntity. A method annotated @ResponseStatus(HttpStatus.CREATED) that returns ResponseEntity.ok(...) answers 200, because the entity's status is applied last. Pick one style per method so readers are not misled.
Why does my @ResponseStatus exception return 500?
Usually a broader @ExceptionHandler (for Exception or RuntimeException) caught it first. Handlers take precedence over the annotation.
More examples
ResponseStatusException for one-off cases
Throwing ResponseStatusException sets the status without declaring an exception class. Spring turns it into a response with that status; whether the message is shown depends on your error configuration.
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
record Voucher(String code, int percentOff) {}
@RestController
public class VoucherController {
// No custom exception class needed for a one-off status.
@GetMapping("/api/vouchers/{code}")
Voucher voucher(@PathVariable String code) {
if (!code.startsWith("SAVE")) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Unknown voucher " + code);
}
return new Voucher(code, 10);
}
}
An exception that carries its own problem detail
ErrorResponseException implements ErrorResponse. When a ResponseEntityExceptionHandler is registered (your own advice, or Boot's when spring.mvc.problemdetails.enabled=true), its ProblemDetail is written as application/problem+json with the type, title and custom sku property, and no extra handler is needed.
import java.net.URI;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.ErrorResponseException;
// The exception carries a full problem detail. ResponseEntityExceptionHandler
// (yours, or Boot's with spring.mvc.problemdetails.enabled=true) renders it as is.
public class InsufficientStockException extends ErrorResponseException {
public InsufficientStockException(String sku, int requested, int available) {
super(HttpStatus.CONFLICT, problem(sku, requested, available), null);
}
private static ProblemDetail problem(String sku, int requested, int available) {
ProblemDetail problem = ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT,
"Only " + available + " of " + sku + " left, " + requested + " requested.");
problem.setType(URI.create("https://api.example.com/problems/insufficient-stock"));
problem.setTitle("Insufficient stock");
problem.setProperty("sku", sku);
return problem;
}
}
Why reason breaks a success response
With reason set, Spring calls sendError(201, "Receipt created") and never writes the Receipt. The client sees an error-style body under a success status. Drop the reason attribute.
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
record Receipt(String id) {}
@RestController
public class ReasonPitfallController {
// Do not do this: with reason set, Spring calls sendError(),
// the returned Receipt is discarded and the client gets an error page body.
@PostMapping("/api/receipts")
@ResponseStatus(code = HttpStatus.CREATED, reason = "Receipt created")
Receipt create() {
return new Receipt("r-1");
}
}
Discussion