Skip to main content
Advertisement

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.

SituationAppropriate Status Code
Resource not found404 Not Found
New resource created successfully201 Created
Authentication failure401 Unauthorized
Invalid request data400 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

@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: ResponseEntity lets 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.

Advertisement