📦 HttpEntity<T> — Headers + Body, No Extras¶
Essence:
HttpEntity<T>is the minimal HTTP envelope: headers + body. No status, no method, no URI. It’s the base building block used byRequestEntity(adds method+URI) andResponseEntity(adds status).
1) Where it lives¶
- Package:
org.springframework.http - Role: Base class for HTTP message content (request or response)
- Used by: Spring MVC & WebFlux,
RestTemplate, message converters
2) Anatomy¶
Immutable once created.
3) Why/when to use it¶
| Situation | Use HttpEntity<T> because… |
|---|---|
| You only need to pass headers + body (no method/URI/status) | It’s the smallest correct abstraction |
| You’re inside a controller and want access to inbound headers + body | Accept HttpEntity<T> as a parameter |
You’re making a client call where some APIs accept HttpEntity |
Clean way to attach headers to a body |
If you need method+URI, use RequestEntity<T>.
If you need status, use ResponseEntity<T>.
4) Create it¶
var headers = new HttpHeaders();
headers.set("X-Trace-Id", traceId);
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<MyDto> entity = new HttpEntity<>(payload, headers);
No builder; it’s just a value object.
5) In controllers: read raw request¶
@PostMapping("/ingest")
public ResponseEntity<Void> ingest(HttpEntity<String> request) {
HttpHeaders h = request.getHeaders();
String body = request.getBody();
// validate, log, route...
return ResponseEntity.accepted().build();
}
Works in MVC and WebFlux controllers.
6) With RestTemplate¶
RestTemplate rt = new RestTemplate();
// POST JSON with headers
HttpHeaders h = new HttpHeaders();
h.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<UserCreate> req = new HttpEntity<>(dto, h);
ResponseEntity<User> res = rt.postForEntity(url, req, User.class);
For GET with headers but no body:
HttpHeaders h = new HttpHeaders();
h.setAccept(List.of(MediaType.APPLICATION_JSON));
HttpEntity<Void> req = new HttpEntity<>(h);
ResponseEntity<User> res = rt.exchange(url, HttpMethod.GET, req, User.class);
7) Generics: keep types honest¶
HttpEntity<List<User>> entity = new HttpEntity<>(users, headers);
// Body type drives message converter selection (JSON, XML, etc.)
For responses with parameterized types, you still need ParameterizedTypeReference at the ResponseEntity side (client read path), not here.
8) Content negotiation & converters¶
-
Spring picks a
HttpMessageConverterbased on: -
The body type
T Content-Type(outbound) /Accept(inbound)HttpEntityjust carries the data; converters do the serialization/deserialization.
9) Compared with siblings¶
| Type | Direction | Adds on top of HttpEntity |
Typical use |
|---|---|---|---|
HttpEntity<T> |
Request or Response | — | Minimal headers+body wrapper |
RequestEntity<T> |
Request | method + URI | Outbound requests, controller param with method/URI |
ResponseEntity<T> |
Response | status | Controller return value, client result |
10) Common patterns¶
Upload binary
HttpHeaders h = new HttpHeaders();
h.setContentType(MediaType.APPLICATION_OCTET_STREAM);
HttpEntity<byte[]> req = new HttpEntity<>(bytes, h);
rt.exchange(url, HttpMethod.POST, req, Void.class);
Multipart (file + JSON)
MultiValueMap<String,Object> parts = new LinkedMultiValueMap<>();
parts.add("file", new ByteArrayResource(bytes){ @Override public String getFilename(){ return "data.csv"; }});
parts.add("meta", new HttpEntity<>(Map.of("source","import"),
new HttpHeaders(){ { setContentType(MediaType.APPLICATION_JSON); } }));
HttpHeaders h = new HttpHeaders();
h.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String,Object>> req = new HttpEntity<>(parts, h);
rt.postForEntity(url, req, Void.class);
Conditional GET
HttpHeaders h = new HttpHeaders();
h.setIfNoneMatch(etag);
HttpEntity<Void> req = new HttpEntity<>(h);
ResponseEntity<Resource> res = rt.exchange(url, HttpMethod.GET, req, Resource.class);
// 304 if not modified
11) Pitfalls (don’t do these)¶
| Mistake | Why it bites | Do instead |
|---|---|---|
Returning HttpEntity from a controller expecting status control |
You can’t set status here | Return ResponseEntity<T> |
Trying to set method/URI on HttpEntity |
It doesn’t have them | Use RequestEntity<T> |
Forgetting Content-Type on POST/PUT |
415 Unsupported Media Type | headers.setContentType(...) |
| Mutating headers after sending | Too late / ignored | Build headers before creating/sending |
12) Quick reference¶
// Make one
HttpEntity<T> e1 = new HttpEntity<>(body);
HttpEntity<T> e2 = new HttpEntity<>(body, headers);
HttpEntity<Void> e3 = new HttpEntity<>(headers);
// Controller reads inbound
ResponseEntity<?> handle(HttpEntity<String> req) { ... }
// RestTemplate call with headers
rt.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), Resp.class);
rt.postForEntity(url, new HttpEntity<>(dto, headers), Resp.class);
13) Mental model¶
It’s the plain envelope. Reach for RequestEntity when you need route/method; reach for ResponseEntity when you need status semantics. Keep your API surface honest and your HTTP concerns explicit.