Skip to main content

3.4 ResponseEntity & Content Negotiation

← Back to Chapter 3: REST API & Web Layer

ResponseEntity<T> represents the entire HTTP response: status code, headers, and body. It gives engineers total programmatic control over responses sent to clients.


1. HTTP Status Code Conventions​

Using standard HTTP status codes correctly ensures clients can handle API outcomes predictably:

Code RangeCategoryCommon REST Usages
200 OKSuccessStandard response for successful GET, PUT, or PATCH.
201 CreatedSuccessSuccessful POST creating a resource. Must include Location header.
204 No ContentSuccessSuccessful DELETE or action where no body needs to be returned.
400 Bad RequestClient ErrorValidation failures, malformed JSON, missing required headers.
401 UnauthorizedClient ErrorMissing or invalid authentication credentials (JWT missing/expired).
403 ForbiddenClient ErrorAuthenticated user lacks permission/role for this resource.
404 Not FoundClient ErrorTarget resource identifier does not exist.
409 ConflictClient ErrorDuplicate unique key (e.g., email already registered).
500 Internal ErrorServer ErrorUnhandled runtime exceptions, database failures.

2. Constructing Responses with Fluent API​

@RestController
@RequestMapping("/api/v1/accounts")
public class AccountController {

// 200 OK with custom headers
@GetMapping("/{id}")
public ResponseEntity<AccountDTO> getAccount(@PathVariable Long id) {
AccountDTO dto = accountService.findById(id);
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS))
.header("X-App-Version", "2.1.0")
.body(dto);
}

// 201 Created with Location URI
@PostMapping
public ResponseEntity<AccountDTO> createAccount(@Valid @RequestBody AccountRequest req) {
AccountDTO created = accountService.create(req);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(created.id())
.toUri();
return ResponseEntity.created(location).body(created);
}

// 204 No Content for deletion
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteAccount(@PathVariable Long id) {
accountService.delete(id);
return ResponseEntity.noContent().build();
}
}

3. Content Negotiation​

Spring automatically inspects the client's Accept header to decide serialization format:

  • Client sends Accept: application/json ➔ Jackson serializes to JSON.
  • Client sends Accept: application/xml ➔ Jackson XML module produces XML.
@GetMapping(value = "/{id}", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public AccountDTO getNegotiated(@PathVariable Long id) {
return accountService.findById(id);
}