3.4 Full HTTP Response Control with ResponseEntity
1. The Limitation of Simple Returns
Up until now, simply returning an object from a controller caused Spring to automatically respond with 200 OK and the serialized JSON body.
@GetMapping("/api/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // Always returns 200 OK
}
However, real-world APIs need to return the appropriate HTTP status code depending on the situation.
| Situation | Appropriate Status Code |
|---|---|
| Resource not found | 404 Not Found |
| New resource created successfully | 201 Created |
| Authentication failure | 401 Unauthorized |
| Invalid request data | 400 Bad Request |
This is where ResponseEntity<T> comes in.
2. What is ResponseEntity?
ResponseEntity<T> is a Spring response wrapper that gives you full control over all three parts of an HTTP response: Status Code, ** Headers**, and ** Body**.
ResponseEntity<T>
├── Status Code (200, 201, 400, 404, etc.)
├── Headers (Content-Type, Location, etc.)
└── Body (Response data of type T)
3. Basic Usage
3-1. Static Factory Methods (Recommended)
@GetMapping("/api/users/{id}")
public ResponseEntity<UserResponseDto> getUser(@PathVariable Long id) {
UserResponseDto user = userService.findById(id);
if (user == null) {
// Return 404 with no body
return ResponseEntity.notFound().build();
}
// Return 200 OK with JSON body
return ResponseEntity.ok(user);
}
3-2. Resource Creation (201 Created)
@PostMapping("/api/users")
public ResponseEntity<UserResponseDto> createUser(@RequestBody UserCreateRequestDto request) {
UserResponseDto created = userService.create(request);
// 201 Created + Location header pointing to the new resource
URI location = URI.create("/api/users/" + created.getId());
return ResponseEntity.created(location).body(created);
}
3-3. Commonly Used Static Methods
ResponseEntity.ok(body) // 200 OK
ResponseEntity.ok().build() // 200 OK (no body)
ResponseEntity.created(uri).body(body) // 201 Created
ResponseEntity.noContent().build() // 204 No Content (for DELETE, etc.)
ResponseEntity.badRequest().body(body) // 400 Bad Request
ResponseEntity.notFound().build() // 404 Not Found
ResponseEntity.status(HttpStatus.FORBIDDEN).body(body) // custom status code
4. Builder Style (Fine-grained Control)
Use the builder pattern when you need precise control over status code, headers, and body together.
@GetMapping("/api/users/{id}")
public ResponseEntity<UserResponseDto> getUser(@PathVariable Long id) {
UserResponseDto user = userService.findById(id);
return ResponseEntity
.status(HttpStatus.OK) // Specify status code
.header("X-Custom-Header", "value") // Add custom header
.body(user); // Set response body
}
5. DELETE API Example (204 No Content)
On successful deletion, REST API convention is to return 204 No Content with an empty body.
@DeleteMapping("/api/users/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build(); // 204 No Content
}
Key Takeaway:
ResponseEntitylets you go beyond simple object returns and design precise, standards-compliant HTTP responses with the correct status codes and headers. In production code, it's used in virtually every API method.