A controller can return an ETag so a later matching If-None-Match GET receives 304 without a response body.
Spring MVC conditional GET: validate the representation tag
The downloadable Spring source kit checks this boundary with the named JUnit test and its pinned dependencies.
The tag belongs to the representation
The fixture serves a fixed receipt version with ETag receipt-7-v3. A matching conditional GET returns 304 and an empty body. A stale tag returns 200 and the current body. A missing receipt remains 404; a cache tag cannot turn an absent resource into a valid one.
The code uses Cache-Control: no-cache so a cache may store the representation but must revalidate it before reuse. In a real service, compute a tag from the correct version of the selected representation, including any user-specific visibility boundary. A fixed literal is only a teaching fixture.
Bandwidth is not authorization
An ETag is a cache validator. It is not proof that the caller may see a receipt. A reverse proxy or shared cache must respect authentication and cache-control rules. An If-Match write precondition is a separate mutation contract, not implemented by this GET test.
Checked source
@GetMapping("/contract/receipts/{id}")
ResponseEntity<String> read(@PathVariable int id) {
if (id != 7) return ResponseEntity.notFound().build();
return ResponseEntity.ok()
.cacheControl(CacheControl.noCache())
.eTag("receipt-7-v3")
.body("receipt:7:reviewed");
}Verification boundary
ConditionalReceiptHttpTest.returnsA304OnlyForTheMatchingRepresentationTag runs in the Spring source kit. This excerpt omits imports and surrounding test setup; the downloadable kit contains the complete source.
Costs and boundaries
MockMvc checks the response status, tag and body for current, matching and stale requests. This fixed in-process controller does not measure upstream computation, proxy caching, deployment behavior or private-data isolation.
Common Mistakes
- Do not treat ETag possession as permission to read a resource.
- Do not reuse one tag across different visible representations.
- Do not claim a shallow ETag filter avoids generating the response body.
Read next
Spring MVC request validation: reject invalid commands before mutation, Spring API pagination: bounded requests and immutable snapshots, Spring Security filter chain: authentication, CSRF and request order.
Continue with range and graph boundaries
Continue with Spring If-Match writes: reject a stale receipt revision.
