Loslegen
Javaneer
Zurück zur Stufe
Modul 2·Web-APIs bauen

Request- & Response-Bodies

DTOs und Records, @RequestBody, JSON-(De-)Serialisierung mit Jackson und Content Negotiation.

16 Min. LesezeitFortgeschritten

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

Your endpoints exchange data with clients as JSON. Spring MVC converts between JSON and Java objects automatically - but which objects you expose matters a lot for a clean, safe API. Enter DTOs.

@RequestBody: JSON in

When a client POSTs JSON, @RequestBody tells Spring to deserialize it into a Java object using Jackson:

@PostMapping
ResponseEntity<BookResponse> add(@RequestBody CreateBookRequest request) {
    // `request` is already a populated Java object — Jackson parsed the JSON
    ...
}

Records are perfect for this - immutable, concise, and Jackson binds JSON fields to record components by name:

record CreateBookRequest(String isbn, String title, String author) {}

Why not just expose your entity?

It's tempting to accept and return your domain Book directly. Resist it. Separating API models (DTOs) from your domain/persistence model buys you:

  • Safety - clients can't set fields they shouldn't (an id, an internal flag).
  • Stability - you can refactor internals without breaking the public JSON shape.
  • Clarity - the request shape (what you accept) and response shape (what you return) can differ from each other and from storage.
DTOs are the menu, not the kitchen

A restaurant's menu is a clean, curated view of what guests can order - it isn't the kitchen's messy inventory of raw ingredients, supplier codes, and prep notes. DTOs are the menu: they present exactly what the API offers and accepts. The kitchen (your entities and internals) can be reorganized freely without ever reprinting the menu.

BookVault uses a small request DTO and response DTO:

record CreateBookRequest(String isbn, String title, String author) {}
record BookResponse(String isbn, String title, String author, boolean available) {}

Content negotiation

How does Spring know to produce JSON? Content negotiation. The client's Accept header (e.g. Accept: application/json) tells Spring which HttpMessageConverter to use. JSON is the default in a Boot web app, so it "just works" - but the same controller could serve XML if you added that converter and the client asked for it.

Jackson is highly configurable

Field naming, date formats, ignoring unknown properties, omitting nulls - all are Jackson settings you tune once (globally in application.yml or via an ObjectMapper bean) instead of per-endpoint. Boot auto-configures a sensible Jackson by default.

Spot the leak

A teammate's endpoint accepts and returns the JPA Book entity directly. A client discovers they can POST {"id": 999, "title": "X", "internalNotes": "hack"} and set the database id and an internal field. Explain how a request DTO fixes this, and what the DTO should (and shouldn't) contain.

Why expose DTOs instead of your persistence entities directly in a REST API?

Key takeaways

  • @RequestBody deserializes incoming JSON into a Java object (records work great); return values are serialized to JSON by Jackson.
  • Expose DTOs, not your domain/persistence entities: it's safer (no forbidden fields), more stable (refactor internals freely), and clearer.
  • Use distinct request and response DTOs when what you accept differs from what you return.
  • Content negotiation (the Accept header) picks the HttpMessageConverter; JSON via Jackson is the Boot default.
  • Configure Jackson once globally rather than per endpoint.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten