🔢 B8 · Status & ResponseEntity: 200/201/204
B8 · Status & ResponseEntity: `ResponseEntity` is the tool with which the developer takes **full control of the response**: it consciously chooses not just the body but the statu
`ResponseEntity` is the tool with which the developer takes **full control of the response**: it consciously chooses not just the body but the status code and headers. Even success is not a single code: **200 OK** = "here is the result" (GET), **201 Created** = "I created a new resource, its address is in the Location header" (POST), **204 No Content** = "I did it but there's no body to return" (DELETE/some PUT). But if all are "success", couldn't we just return 200 — the client works anyway? Often it "works" but the contract breaks: if a POST returns 200 instead of 201, an automation chain that reads the new record's address from the `Location` header breaks; if a DELETE returns 200 + empty body instead of 204, a client trying to parse the body may error. In Java the equivalent is the conscious difference between `return bug;` (Spring assumes 200) and `ResponseEntity.status(201).header("Location", ...).body(bug)`. In QA the status code is a **semantic contract**: the tester tests not just "did it succeed" but "is it the CORRECT success code" — because a wrong-but-2xx code is a contract defect that silently breaks client automations.
Choosing the Right Success Code
**🐞 Defect Birth — if POST returns 200 instead of 201 (no Location)** **Code:** `@PostMapping public Bug create(...) { return service.create(req); }` — Spring returns 200 for this, with NO `Location` header. **What happens:** an automation test creating a new bug expects the address in the `Location` header (`/api/v1/bugs/42`) to read the record in the next step. With no header the chain breaks; or a different client trying to read the new id from the body sees 200 and thinks "this was an update, not a creation". **Why sneaky:** the record is truly created, the body returns, the status is 2xx — at a glance "success". The problem only appears when a client chain that RELIES on 201/Location runs; simple tests miss it. **Where the tester catches it:** with a contract test verifying the status is exactly 201 AND the `Location` header gives the new record's address. The "2xx = passed" assumption hides this defect.
🎬 200 or 201? The Location Header and the Broken Chain
An automation chain: first create a bug with POST, then read it with GET from the returned address. Everything hinges on the right status code.
The right path — POST returns 201 Created and the Location header gives the new record's address: /api/v1/bugs/42. The chain uses this address.
The wrong path — the developer wrote `return bug`, Spring returns 200 and there is NO Location header. The record is created but the address is lost.
The next request tries to read the address from Location but there is none — the chain breaks. The test does not look like "create failed", it says "next step is null".
The lesson — 2xx is not enough; the CORRECT 2xx is required. POST=201+Location, GET=200, DELETE=204. The tester verifies the status code and Location separately.
Which Operation Returns Which Code?
Successful read is 200 + body. Absent record is 404 (NOT 200 + empty body).
Creation returns 201 + Location header; the client reads the new record's address here.
DELETE → 204 No Content…
Successful delete is 204 (no body). The client must not try to parse a body.