Zum Hauptinhalt springen

Woche 4, Tag 3 — Request- und Response-Bodies

Ziel

Heute verstehst du, wie Spring MVC Request Bodies und Response Bodies verarbeitet.

Die Kernfragen:

  1. Was ist ein HTTP Request Body?
  2. Was ist ein HTTP Response Body?
  3. Was macht @RequestBody?
  4. Was macht @ResponseBody?
  5. Warum liefert @RestController automatisch JSON zurück?
  6. Was ist HttpMessageConverter?
  7. Was ist Jackson?
  8. Was sind DTOs?
  9. Warum sollten REST APIs meist DTOs statt Entities zurückgeben?
  10. Wie funktioniert Validation mit @Valid?
  11. Was ist ResponseEntity?
  12. Welche typischen Prüfungsfallen gibt es?

1. Kurz-Wiederholung von Woche 4, Tag 2

An Tag 2 hast du gelernt:

  • @RequestMapping mappt HTTP Requests auf Controller-Methoden.
  • @GetMapping, @PostMapping, @PutMapping, @PatchMapping und @DeleteMapping sind Shortcut-Annotations.
  • @PathVariable liest Werte aus dem Path.
  • @RequestParam liest Query Parameters.
  • @RequestHeader liest Headers.
  • @RequestBody liest den HTTP Body.
  • consumes prüft den Request Content-Type.
  • produces beschreibt den Response Media Type.
  • Content-Type bedeutet: Format des Request Bodies.
  • Accept bedeutet: gewünschtes Response-Format.

Merksatz:


PathVariable = path.
RequestParam = query.
RequestHeader = header.
RequestBody = body.

Heute gehst du tiefer in Request- und Response-Bodies ein.


2. Was ist ein HTTP Request Body?

Der Request Body ist die Datenmenge, die der Client an den Server sendet.

Beispiel:


POST /api/tasks
Content-Type: application/json

{
"title": "Learn Spring MVC",
"priority": "HIGH"
}

Der Body ist:


{
"title": "Learn Spring MVC",
"priority": "HIGH"
}

Der Body wird meist genutzt bei:


POST
PUT
PATCH

Häufige Request-Body-Formate:


JSON
XML
Form Data
Multipart File Upload
Plain Text

Bei REST APIs ist JSON am häufigsten.


3. Was ist ein HTTP Response Body?

Der Response Body ist die Datenmenge, die der Server an den Client zurücksendet.

Beispiel-Response:


HTTP/1.1 201 Created
Content-Type: application/json

{
"id": 1,
"title": "Learn Spring MVC",
"priority": "HIGH"
}

Der Response Body ist:


{
"id": 1,
"title": "Learn Spring MVC",
"priority": "HIGH"
}

Merksatz:

Der Request Body kommt vom Client. Der Response Body geht zurück zum Client.


4. @RequestBody

@RequestBody sagt Spring:

Lies den HTTP Request Body und konvertiere ihn in ein Java-Objekt.

Request:


POST /api/tasks
Content-Type: application/json

{
"title": "Learn Spring MVC"
}

DTO:


public record CreateTaskRequest(String title) {
}

Controller:


@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

Spring konvertiert JSON in:


CreateTaskRequest

Merksatz:

@RequestBody bedeutet: JSON Body → Java-Objekt.


5. Was passiert intern mit @RequestBody?

Ablauf:


1. Client sendet JSON Body.
2. Spring MVC empfängt den Request.
3. Controller-Methode hat @RequestBody.
4. Spring prüft Content-Type.
5. Spring findet einen passenden HttpMessageConverter.
6. Jackson konvertiert JSON in ein Java-Objekt.
7. Die Controller-Methode erhält das Java-Objekt.

Beispiel:


{
"title": "Learn Spring"
}

wird zu:


new CreateTaskRequest("Learn Spring")

Mit Java Records kann Spring/Jackson das Record aus den JSON-Feldern erzeugen.


6. @RequestBody und Content-Type

Der Client sollte senden:


Content-Type: application/json

Das sagt Spring:


Der Request Body ist JSON.

Dann kann Spring den JSON Converter wählen.

Erwartet der Endpoint JSON, der Client aber sendet:


Content-Type: text/plain

kann Spring antworten mit:


415 Unsupported Media Type

Merksatz:

Content-Type sagt Spring, wie der Request Body gelesen werden soll.


7. Ungültiger JSON Body

Request:


POST /api/tasks
Content-Type: application/json

{
"title":
}

Dieses JSON ist ungültig.

Spring kann es nicht in ein Java-Objekt konvertieren.

Übliche Response:


400 Bad Request

Warum?

Weil der Client ungültige Request-Daten gesendet hat.


8. Fehlendes @RequestBody

Problem:


@PostMapping("/api/tasks")
public TaskDto create(CreateTaskRequest request) {
return taskService.create(request);
}

Request:


POST /api/tasks
Content-Type: application/json

{
"title": "Learn Spring"
}

Ohne @RequestBody liest Spring das JSON möglicherweise nicht aus dem Body in CreateTaskRequest.

Fix:


@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

Merksatz:

Für einen JSON Request Body nutze @RequestBody.


9. Ein Request Body

Üblicherweise sollte eine Controller-Methode genau ein @RequestBody haben.

Schlecht:


@PostMapping("/api/tasks")
public TaskDto create(
@RequestBody CreateTaskRequest request,
@RequestBody AuditInfo auditInfo
) {
return taskService.create(request, auditInfo);
}

Warum schlecht?


Ein HTTP Request hat einen Body.
Spring kann einen Body normalerweise nicht in zwei separate @RequestBody-Objekte aufteilen.

Besser:


public record CreateTaskCommand(
String title,
AuditInfo auditInfo
) {
}

Controller:


@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskCommand command) {
return taskService.create(command);
}

Merksatz:

Ein HTTP Request Body sollte auf ein Request-Objekt mappen.


10. @ResponseBody

@ResponseBody sagt Spring:

Schreibe den Rückgabewert der Methode direkt in den HTTP Response Body.

Beispiel:


@Controller
public class HelloController {

@GetMapping("/api/hello")
@ResponseBody
public String hello() {
return "hello";
}
}

Response Body:


hello

Ohne @ResponseBody kann Spring "hello" als View-Name behandeln.


11. @RestController enthält @ResponseBody

Sehr wichtiger Prüfungssatz:


@RestController = @Controller + @ResponseBody

Das hier:


@RestController
public class HelloController {

@GetMapping("/api/hello")
public String hello() {
return "hello";
}
}

bedeutet: Der Rückgabewert landet direkt im Response Body.

Der Response Body ist also:


hello

Spring sucht keine View namens hello.


12. Response Body mit Java-Objekt

Controller:


@RestController
@RequestMapping("/api/tasks")
public class TaskController {

@GetMapping("/{id}")
public TaskDto get(@PathVariable Long id) {
return new TaskDto(id, "Learn Spring MVC");
}
}

DTO:


public record TaskDto(Long id, String title) {
}

Response:


{
"id": 1,
"title": "Learn Spring MVC"
}

Spring konvertiert das Java-Objekt automatisch zu JSON.


13. Wer konvertiert Java-Objekte zu JSON?

Der Converter ist:


HttpMessageConverter

Für JSON nutzt Spring Boot üblicherweise:


Jackson

Ablauf:


Java-Objekt -> HttpMessageConverter -> JSON Response Body

Merksatz:

HttpMessageConverter konvertiert zwischen HTTP Bodies und Java-Objekten.


14. HttpMessageConverter

HttpMessageConverter konvertiert in zwei Richtungen:


Request Body -> Java-Objekt
Java-Objekt -> Response Body

Beispiele:


JSON -> Java-Objekt
Java-Objekt -> JSON
String -> text/plain
byte[] -> binäre Response
Form Data -> Java-Objekt oder Parameter

Für eine REST API:


JSON-Konvertierung ist am wichtigsten.

15. Jackson

Jackson ist eine Java Library für JSON Serialization und Deserialization.

Serialization bedeutet:


Java-Objekt -> JSON

Deserialization bedeutet:


JSON -> Java-Objekt

Beispiel Serialization:


new TaskDto(1L, "Learn Spring")

wird zu:


{
"id": 1,
"title": "Learn Spring"
}

Beispiel Deserialization:


{
"title": "Learn Spring"
}

wird zu:


CreateTaskRequest

Merksatz:

Jackson liest JSON in Java und schreibt Java in JSON.


16. Spring Boot und Jackson

Wenn du hinzufügst:


implementation("org.springframework.boot:spring-boot-starter-web")

bringt und konfiguriert Spring Boot Jackson üblicherweise automatisch.

Deshalb funktioniert das ohne manuelles ObjectMapper-Setup:


@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

Spring Boot konfiguriert die JSON-Konvertierung automatisch.


17. DTOs

DTO bedeutet:


Data Transfer Object

Kurze Definition:

Ein DTO ist ein Objekt, das Daten zwischen Layern oder über das Netzwerk transportiert.

In REST APIs werden DTOs für Request- und Response-Bodies genutzt.

Request DTO:


public record CreateTaskRequest(
String title,
String priority
) {
}

Response DTO:


public record TaskDto(
Long id,
String title,
String priority,
String status
) {
}

Merksatz:

DTOs definieren den API Contract.


18. Request DTO vs Response DTO

Das Request DTO beschreibt, was der Client sendet.


public record CreateTaskRequest(
String title,
String priority
) {
}

Das Response DTO beschreibt, was der Server zurückgibt.


public record TaskDto(
Long id,
String title,
String priority,
String status
) {
}

Sie müssen nicht gleich sein.

Beispiel:

Der Client sendet:


{
"title": "Learn Spring",
"priority": "HIGH"
}

Der Server gibt zurück:


{
"id": 100,
"title": "Learn Spring",
"priority": "HIGH",
"status": "OPEN"
}

19. Warum DTOs nutzen?

DTOs helfen bei:


stabilen API Contracts
Verbergen der internen Database Structure
Vermeiden von Leaks sensibler Felder
Vermeiden von Lazy Loading Serialization Problemen
Steuerung der Response-Form
Trennung von API und Persistence Model
Validation
Versionierung

Merksatz:

DTOs schützen die API-Grenze.


20. Entity vs DTO

Entity-Beispiel:


@Entity
public class UserEntity {

@Id
private Long id;

private String email;

private String passwordHash;

private boolean internalAdminFlag;
}

Schlechter Controller:


@GetMapping("/api/users/{id}")
public UserEntity getUser(@PathVariable Long id) {
return userRepository.findById(id).orElseThrow();
}

Problem:


passwordHash kann preisgegeben werden
interne Felder können leaken
Database Model wird zum API Contract
Lazy Relationships können Serialization brechen

Besseres Response DTO:


public record UserDto(
Long id,
String email
) {
}

Controller:


@GetMapping("/api/users/{id}")
public UserDto getUser(@PathVariable Long id) {
return userService.findById(id);
}

Merksatz:

Gib deine Database Entity nicht als öffentliche API preis.


21. Entity zu DTO mappen

Entity:


public class TaskEntity {

private Long id;
private String title;
private String status;

public Long getId() {
return id;
}

public String getTitle() {
return title;
}

public String getStatus() {
return status;
}
}

DTO:


public record TaskDto(
Long id,
String title,
String status
) {
}

Manuelles Mapping:


public TaskDto toDto(TaskEntity entity) {
return new TaskDto(
entity.getId(),
entity.getTitle(),
entity.getStatus()
);
}

Für die Prüfung reicht manuelles Mapping zum Verständnis.

In echten Projekten nutzen manche Teams Mapping Libraries — manuelles Mapping ist aber oft klar und sicher.


22. Request DTO sollte keine Entity sein

Schlecht:


@PostMapping("/api/tasks")
public TaskEntity create(@RequestBody TaskEntity taskEntity) {
return taskRepository.save(taskEntity);
}

Warum schlecht?


Client kann Felder senden, die er nicht steuern sollte
Entity Structure leakt in die API
Validation wird unklar
Security Risk durch interne Felder

Besser:


public record CreateTaskRequest(
String title,
String priority
) {
}

Controller:


@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

23. Validation

Validation prüft, ob Request-Daten gültig sind.

Beispiel-Regeln:


title darf nicht leer sein
email muss gültig sein
age muss mindestens 18 sein
price muss positiv sein
date muss in der Zukunft liegen
list darf nicht leer sein

In Spring Boot nutzt Validation üblicherweise Bean Validation Annotations.

Dependency nötig:


implementation("org.springframework.boot:spring-boot-starter-validation")

Merksatz:

Nutze Validation, um schlechte Client-Eingaben früh abzulehnen.


24. Häufige Validation Annotations

Beispiele:


@NotNull
@NotBlank
@NotEmpty
@Size
@Min
@Max
@Positive
@PositiveOrZero
@Email
@Pattern
@Past
@Future

Häufige Bedeutung:

AnnotationBedeutung
@NotNullWert darf nicht null sein
@NotBlankString muss Text ohne nur Whitespace enthalten
@NotEmptyCollection/String darf nicht leer sein
@SizeGröße muss im Bereich liegen
@MinZahl muss mindestens den Wert haben
@MaxZahl darf höchstens den Wert haben
@EmailString muss Email-Format haben
@PositiveZahl muss größer als null sein

25. @NotNull vs @NotBlank vs @NotEmpty

Das ist eine Prüfungsfalle.

@NotNull

Der Wert darf nicht null sein.


@NotNull
private String name;

Ungültig:


null

Aber das kann gültig sein:


""
" "

@NotEmpty

Der Wert darf nicht null oder leer sein.

Ungültig:


null
""
leere Liste

Aber das kann für String gültig sein:


" "

@NotBlank

Für Strings.

Ungültig:


null
""
" "

Merksatz:

Für Pflicht-Textfelder nutze @NotBlank.


26. Validation-Beispiel

Request DTO:


public record CreateTaskRequest(

@NotBlank
@Size(max = 100)
String title,

@NotBlank
String priority
) {
}

Controller:


@PostMapping("/api/tasks")
public ResponseEntity<TaskDto> create(
@Valid @RequestBody CreateTaskRequest request
) {
TaskDto created = taskService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}

Wichtig:


Validation läuft wegen @Valid.

Ohne @Valid werden die Annotations in der Controller-Methode möglicherweise nicht geprüft.

Merksatz:

Validation Annotations definieren Regeln. @Valid triggert Validation.


27. Was passiert, wenn Validation fehlschlägt?

Request:


POST /api/tasks
Content-Type: application/json

{
"title": "",
"priority": "HIGH"
}

DTO:


public record CreateTaskRequest(
@NotBlank String title,
@NotBlank String priority
) {
}

Controller:


@PostMapping("/api/tasks")
public TaskDto create(@Valid @RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

Weil title leer ist, schlägt Validation fehl.

Übliche Response:


400 Bad Request

Spring wirft eine validation-bezogene Exception.

Später lernst du, wie du Validation Error Responses mit @ControllerAdvice anpassen kannst.


28. Verschachtelte Validation

Request DTO:


public record CreateClientRequest(

@NotBlank
String name,

@Valid
AddressRequest address
) {
}

Verschachteltes DTO:


public record AddressRequest(

@NotBlank
String street,

@NotBlank
String city
) {
}

Wichtig:


Nutze @Valid auf dem verschachtelten Feld, um das verschachtelte Objekt zu validieren.

Ohne verschachteltes @Valid läuft die Validation des verschachtelten Objekts möglicherweise nicht.

Merksatz:

Nutze @Valid auch für verschachtelte Objekte.


29. Validation auf Listen

Beispiel:


public record CreateOrderRequest(

@NotEmpty
List<@Valid OrderItemRequest> items
) {
}

Verschachteltes Item:


public record OrderItemRequest(

@NotNull
Long productId,

@Positive
int quantity
) {
}

Bedeutung:


items-Liste darf nicht leer sein
jedes Item muss gültig sein
quantity muss positiv sein

30. @Validated

@Valid ist am häufigsten für Request Body Validation.

@Validated ist eine Spring Annotation, die ebenfalls Validation triggern kann und Validation Groups unterstützt.

Beispiel:


@RestController
@Validated
public class TaskController {

@GetMapping("/api/tasks")
public List<TaskDto> list(@RequestParam @Min(0) int page) {
return taskService.findPage(page);
}
}

Für viele Controller Request Bodies reicht:


@Valid @RequestBody CreateTaskRequest request

Prüfungssicherer Satz:

Nutze @Valid mit @RequestBody, um Request DTOs zu validieren. @Validated ist Springs Variante und unterstützt Validation Groups.


31. Validation auf Query Parameters

Beispiel:


@RestController
@Validated
@RequestMapping("/api/tasks")
public class TaskController {

@GetMapping
public List<TaskDto> list(
@RequestParam(defaultValue = "0") @Min(0) int page,
@RequestParam(defaultValue = "20") @Min(1) @Max(100) int size
) {
return taskService.findPage(page, size);
}
}

Wichtig:


Für Methoden-Parameter-Validation wird @Validated auf der Controller-Klasse häufig genutzt.

32. ResponseEntity

ResponseEntity repräsentiert die vollständige HTTP Response.

Es kann steuern:


Status Code
Headers
Body

Beispiel:


@PostMapping("/api/tasks")
public ResponseEntity<TaskDto> create(@Valid @RequestBody CreateTaskRequest request) {
TaskDto created = taskService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}

Response:


HTTP/1.1 201 Created
Content-Type: application/json

{
"id": 1,
"title": "Learn Spring"
}

Merksatz:

ResponseEntity gibt dir Kontrolle über Status, Headers und Body.


33. 200 OK zurückgeben


@GetMapping("/api/tasks/{id}")
public ResponseEntity<TaskDto> get(@PathVariable Long id) {
TaskDto task = taskService.findById(id);
return ResponseEntity.ok(task);
}

Bedeutung:


HTTP 200 OK mit Body

34. 201 Created zurückgeben


@PostMapping("/api/tasks")
public ResponseEntity<TaskDto> create(@Valid @RequestBody CreateTaskRequest request) {
TaskDto created = taskService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}

Bedeutung:


HTTP 201 Created mit Body

Für Create-Operationen ist 201 Created meist besser als 200 OK.


35. Location Header zurückgeben

Für neu erzeugte Resources kannst du einen Location Header zurückgeben.


@PostMapping("/api/tasks")
public ResponseEntity<TaskDto> create(@Valid @RequestBody CreateTaskRequest request) {
TaskDto created = taskService.create(request);

URI location = URI.create("/api/tasks/" + created.id());

return ResponseEntity
.created(location)
.body(created);
}

Response:


HTTP/1.1 201 Created
Location: /api/tasks/1
Content-Type: application/json

Merksatz:

ResponseEntity.created(location) liefert 201 mit einem Location Header.


36. 204 No Content zurückgeben

Delete-Endpoint:


@DeleteMapping("/api/tasks/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
taskService.delete(id);
return ResponseEntity.noContent().build();
}

Bedeutung:


HTTP 204 No Content

Wichtig:


Eine 204 Response sollte keinen Response Body haben.

37. 404 Not Found zurückgeben

Einfaches Beispiel:


@GetMapping("/api/tasks/{id}")
public ResponseEntity<TaskDto> get(@PathVariable Long id) {
Optional<TaskDto> task = taskService.findById(id);

return task
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

Bedeutung:


Task existiert -> 200 OK mit Body
Task existiert nicht -> 404 Not Found

In größeren Apps wird das oft mit Exceptions und @ControllerAdvice gehandhabt.


38. @ResponseStatus

Für feste Status Codes kannst du @ResponseStatus nutzen.

Beispiel:


@PostMapping("/api/tasks")
@ResponseStatus(HttpStatus.CREATED)
public TaskDto create(@Valid @RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

Das liefert:


201 Created

Nutze @ResponseStatus, wenn der Status fest ist.

Nutze ResponseEntity, wenn Status, Headers oder Body mehr Kontrolle brauchen.


39. ResponseEntity vs @ResponseStatus

ThemaResponseEntity@ResponseStatus
Statusdynamisch oder festfest
Headerseinfach steuerbarnicht ideal
BodyexplizitMethoden-Rückgabewert
Häufiger Einsatzflexible Responseseinfacher fester Status
Beispiel200 oder 404immer 201

Merksatz:

Nutze ResponseEntity für Kontrolle. Nutze @ResponseStatus für einen einfachen festen Status.


40. Kompletter Create-Flow mit Validation

Request:


POST /api/tasks
Content-Type: application/json
Accept: application/json

{
"title": "Learn validation",
"priority": "HIGH"
}

Request DTO:


public record CreateTaskRequest(

@NotBlank
@Size(max = 100)
String title,

@NotBlank
String priority
) {
}

Controller:


@PostMapping(
value = "/api/tasks",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<TaskDto> create(
@Valid @RequestBody CreateTaskRequest request
) {
TaskDto created = taskService.create(request);

URI location = URI.create("/api/tasks/" + created.id());

return ResponseEntity.created(location).body(created);
}

Ablauf:


1. Client sendet JSON.
2. Spring prüft Content-Type.
3. Jackson konvertiert JSON zu CreateTaskRequest.
4. Bean Validation prüft @NotBlank und @Size.
5. Controller-Methode läuft.
6. Service erzeugt Task.
7. Controller gibt ResponseEntity zurück.
8. Jackson konvertiert TaskDto zu JSON.
9. Response Status ist 201 Created.
10. Location Header zeigt auf die neue Resource.

41. Typischer Fehler: Validation Starter fehlt

Problem:


public record CreateTaskRequest(
@NotBlank String title
) {
}

Aber die Annotation wird nicht gefunden oder Validation funktioniert nicht.

Mögliche fehlende Dependency:


implementation("org.springframework.boot:spring-boot-starter-validation")

Merksatz:

Für Bean Validation in Spring Boot füge den Validation Starter hinzu.


42. Typischer Fehler: @Valid fehlt

Problem:


@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

DTO:


public record CreateTaskRequest(
@NotBlank String title
) {
}

Die Annotation ist da, aber Validation läuft möglicherweise nicht.

Fix:


@PostMapping("/api/tasks")
public TaskDto create(@Valid @RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

Merksatz:

Füge @Valid hinzu, um Request Body Validation zu triggern.


43. Typischer Fehler: Entity mit Lazy Relations zurückgeben

Entity:


@Entity
public class ClientEntity {

@OneToMany(mappedBy = "client")
private List<TaskEntity> tasks;
}

Controller:


@GetMapping("/api/clients/{id}")
public ClientEntity getClient(@PathVariable Long id) {
return clientRepository.findById(id).orElseThrow();
}

Mögliche Probleme:


Lazy Loading Exception
endlose Rekursion
riesiger Response Body
sensible Felder preisgegeben
API hängt vom Database Model ab

Besser:


public record ClientDto(
Long id,
String name
) {
}

Gib ClientDto zurück.


44. Typischer Fehler: Endlose JSON-Rekursion

Beispiel:


public class Client {
private List<Task> tasks;
}

public class Task {
private Client client;
}

Wenn beide Seiten serialisiert werden:


Client -> tasks -> task -> client -> tasks -> task -> ...

Das kann zu endloser Rekursion führen.

DTOs lösen das sauber, indem sie die Response-Form steuern.

Merksatz:

DTOs verhindern viele JSON Serialization Probleme.


45. Typischer Fehler: Falsche Feldnamen

JSON:


{
"task_title": "Learn Spring"
}

Java Record:


public record CreateTaskRequest(
String title
) {
}

Jackson erwartet:


{
"title": "Learn Spring"
}

Wenn der JSON-Feldname abweicht, wird der Wert möglicherweise nicht wie erwartet gebunden.

Möglicher Fix:


public record CreateTaskRequest(
@JsonProperty("task_title")
String title
) {
}

Dann mappt JSON task_title auf Java title.


46. Häufige Jackson Annotations

Nützliche Annotations:


@JsonProperty
@JsonIgnore
@JsonFormat
@JsonInclude

Beispiele:


public record UserDto(

Long id,

@JsonProperty("email_address")
String email
) {
}

public class InternalUserDto {

private Long id;

@JsonIgnore
private String internalToken;
}

Für die Zertifizierung reicht die Idee:

Jackson Annotations können JSON Serialization und Deserialization anpassen.


47. Datumsangaben und JSON

DTO:


public record TaskDto(
Long id,
String title,
LocalDate dueDate
) {
}

JSON kann so aussehen:


{
"id": 1,
"title": "Learn Spring",
"dueDate": "2026-07-07"
}

Für Date/Time Formatierung nutze wenn möglich Standard-ISO-Formate.

Beispiel für Custom Format:


public record TaskDto(

Long id,

String title,

@JsonFormat(pattern = "yyyy-MM-dd")
LocalDate dueDate
) {
}

48. Request Body und Immutability

Java Records sind gute DTOs, weil sie:


kompakt
immutable
klar
einfach zu serialisieren/deserialisieren
gut für Request/Response-Daten

Beispiel:


public record CreateTaskRequest(
@NotBlank String title,
@NotBlank String priority
) {
}

Für viele moderne Spring Boot Apps sind Records ein sauberer DTO-Stil.


49. Gutes REST Controller Beispiel


@RestController
@RequestMapping("/api/tasks")
public class TaskController {

private final TaskService taskService;

public TaskController(TaskService taskService) {
this.taskService = taskService;
}

@GetMapping("/{id}")
public ResponseEntity<TaskDto> get(@PathVariable Long id) {
return taskService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

@PostMapping(
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<TaskDto> create(
@Valid @RequestBody CreateTaskRequest request
) {
TaskDto created = taskService.create(request);
URI location = URI.create("/api/tasks/" + created.id());

return ResponseEntity.created(location).body(created);
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
taskService.delete(id);
return ResponseEntity.noContent().build();
}
}

Request DTO:


public record CreateTaskRequest(

@NotBlank
@Size(max = 100)
String title,

@NotBlank
String priority
) {
}

Response DTO:


public record TaskDto(
Long id,
String title,
String priority,
String status
) {
}

50. Häufige HTTP Status Codes mit Bodies

SituationStatus
Erfolgreicher GET mit Body200 OK
Erfolgreiches Create201 Created
Erfolgreiches Delete ohne Body204 No Content
Validation Error400 Bad Request
Ungültiges JSON400 Bad Request
Resource nicht gefunden404 Not Found
Nicht unterstützter Request Body Type415 Unsupported Media Type
Nicht unterstützter Response Type angefordert406 Not Acceptable

51. Typische Prüfungsfallen

Falle 1

@RequestBody liest den HTTP Request Body.

Es liest keine Query Parameters.


Falle 2

@ResponseBody schreibt den Rückgabewert in den HTTP Response Body.

Es rendert keine View.


Falle 3

@RestController enthält @ResponseBody.


Falle 4

HttpMessageConverter konvertiert zwischen HTTP Bodies und Java-Objekten.


Falle 5

Jackson übernimmt in Spring Boot MVC Apps üblicherweise JSON.


Falle 6

Content-Type beschreibt das Format des Request Bodies.

Accept beschreibt das Response-Format, das der Client möchte.


Falle 7

Validation Annotations allein reichen nicht.

Nutze @Valid.


Falle 8

Für Bean Validation Support füge spring-boot-starter-validation hinzu.


Falle 9

DTOs sind besser, als JPA Entities direkt preiszugeben.


Falle 10

Nutze ResponseEntity, um Status, Headers und Body zu steuern.


52. Echte Prüfungsfrage: @RequestBody

Frage:

Was macht @RequestBody?

Antwort:

@RequestBody sagt Spring, den HTTP Request Body zu lesen und mit einem HttpMessageConverter in ein Java-Objekt zu konvertieren.


53. Echte Prüfungsfrage: @ResponseBody

Frage:

Was macht @ResponseBody?

Antwort:

@ResponseBody sagt Spring, den Methoden-Rückgabewert direkt in den HTTP Response Body zu schreiben — statt ihn als View-Name zu behandeln.


54. Echte Prüfungsfrage: @RestController

Frage:

Womit ist @RestController gleichwertig?

Antwort:

@RestController ist gleichwertig mit @Controller plus @ResponseBody.


55. Echte Prüfungsfrage: HttpMessageConverter

Frage:

Was macht HttpMessageConverter?

Antwort:

Er konvertiert zwischen HTTP Request/Response Bodies und Java-Objekten — zum Beispiel JSON zu Java-Objekt und Java-Objekt zu JSON.


56. Echte Prüfungsfrage: Jackson

Frage:

Wofür wird Jackson in Spring MVC REST APIs genutzt?

Antwort:

Jackson wird üblicherweise genutzt, um Java-Objekte zu JSON zu serialisieren und JSON in Java-Objekte zu deserialisieren.


57. Echte Prüfungsfrage: DTO

Frage:

Was ist ein DTO?

Antwort:

Ein DTO, oder Data Transfer Object, ist ein Objekt zum Transport von Daten — besonders über API-Grenzen hinweg. In REST APIs beschreiben Request DTOs, was Clients senden, und Response DTOs, was der Server zurückgibt.


58. Echte Prüfungsfrage: Entity vs DTO

Frage:

Warum sollten REST APIs meist DTOs statt JPA Entities zurückgeben?

Antwort:

DTOs schützen die API-Grenze, vermeiden das Preisgeben der internen Database Structure, verhindern Leaks sensibler Felder, vermeiden Lazy Loading- und Endlos-Rekursions-Probleme und erlauben es, den API Contract getrennt vom Persistence Model weiterzuentwickeln.


59. Echte Prüfungsfrage: Validation

Frage:

Wie validierst du einen JSON Request Body?

Antwort:

Füge Validation Annotations zum Request DTO hinzu, füge spring-boot-starter-validation hinzu und nutze @Valid mit @RequestBody.

Beispiel:


public record CreateTaskRequest(
@NotBlank String title
) {
}

@PostMapping("/api/tasks")
public TaskDto create(@Valid @RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

60. Echte Prüfungsfrage: ResponseEntity

Frage:

Warum ResponseEntity nutzen?

Antwort:

ResponseEntity erlaubt einer Controller-Methode, den HTTP Status Code, Response Headers und Response Body zu steuern.


61. Echte Prüfungsfrage: 201 Created

Frage:

Wie gibst du 201 Created mit Body zurück?

Antwort:


return ResponseEntity.status(HttpStatus.CREATED).body(createdDto);

oder mit Location:


return ResponseEntity.created(location).body(createdDto);

62. Echte Prüfungsfrage: 204 No Content

Frage:

Wie gibst du 204 No Content zurück?

Antwort:


return ResponseEntity.noContent().build();

63. Interview-Antwort

Frage:

How does Spring convert a JSON request body into a Java object?

Gute Antwort:

Wenn ein Controller-Methoden-Parameter mit @RequestBody annotiert ist, liest Spring den HTTP Request Body und nutzt einen HttpMessageConverter, um ihn in den Ziel-Java-Typ zu konvertieren. In einer typischen Spring Boot MVC Application ist Jackson auto-configured und wird als JSON Converter genutzt. Der Request Content-Type, zum Beispiel application/json, hilft Spring, den richtigen Converter zu wählen.


64. Interview-Antwort

Frage:

How does Spring convert a Java object into a JSON response?

Gute Antwort:

In einem REST Controller wird der Methoden-Rückgabewert in den Response Body geschrieben, weil @RestController @ResponseBody enthält. Spring nutzt dann einen HttpMessageConverter — üblicherweise Jackson — um das Java-Objekt in JSON zu serialisieren. Das Response-Format kann auch durch Content Negotiation und den Accept Header beeinflusst werden.


65. Interview-Antwort

Frage:

Why do you use DTOs in REST APIs?

Gute Antwort:

Ich nutze DTOs, um einen klaren API Contract zu definieren und die externe API vom internen Persistence Model zu trennen. DTOs helfen, sensible Felder nicht preiszugeben, Lazy Loading- und Endlos-Rekursions-Probleme zu vermeiden, die Response-Form zu steuern und die API einfacher weiterzuentwickeln — ohne Database Entities direkt zu ändern.


66. Interview-Antwort

Frage:

How do you validate request bodies in Spring Boot?

Gute Antwort:

Ich füge spring-boot-starter-validation hinzu, setze Bean Validation Annotations wie @NotBlank, @Size oder @Email auf das Request DTO und annotiere den Controller-Parameter mit @Valid @RequestBody. Schlägt Validation fehl, liefert Spring üblicherweise 400 Bad Request. Für Custom Error Responses kann ich @ControllerAdvice nutzen — das lernen wir später.


67. Interview-Antwort

Frage:

What is ResponseEntity used for?

Gute Antwort:

ResponseEntity repräsentiert die vollständige HTTP Response. Es erlaubt mir, Status Code, Headers und Body zu steuern. Zum Beispiel kann ich nach dem Erzeugen einer Resource 201 Created mit einem Location Header zurückgeben, nach dem Löschen 204 No Content oder 404 Not Found, wenn eine Resource nicht existiert.


68. Kleine Code-Übung

Erstelle Request- und Response-DTOs:


public record CreateTaskRequest(

@NotBlank
@Size(max = 100)
String title,

@NotBlank
String priority
) {
}

public record TaskDto(
Long id,
String title,
String priority,
String status
) {
}

Erstelle den Controller:


@RestController
@RequestMapping("/api/tasks")
public class TaskController {

@PostMapping
public ResponseEntity<TaskDto> create(
@Valid @RequestBody CreateTaskRequest request
) {
TaskDto created = new TaskDto(
1L,
request.title(),
request.priority(),
"OPEN"
);

URI location = URI.create("/api/tasks/" + created.id());

return ResponseEntity.created(location).body(created);
}
}

Fragen:

  1. Was macht @RequestBody?
  2. Was macht @Valid?
  3. Welcher Status Code wird zurückgegeben?
  4. Welcher Header wird zurückgegeben?
  5. Wer konvertiert TaskDto zu JSON?

Antworten:

  1. Es liest den JSON Request Body in CreateTaskRequest.
  2. Es triggert Validation.
  3. 201 Created.
  4. Location: /api/tasks/1.
  5. HttpMessageConverter, üblicherweise Jackson.

69. Kleine Bug-Übung 1

Problem:


@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

DTO:


public record CreateTaskRequest(
@NotBlank String title
) {
}

Request:


{
"title": ""
}

Validation passiert nicht.

Frage:

Was fehlt?

Antwort:

@Valid fehlt.

Fix:


@PostMapping("/api/tasks")
public TaskDto create(@Valid @RequestBody CreateTaskRequest request) {
return taskService.create(request);
}

70. Kleine Bug-Übung 2

Problem:


@GetMapping("/api/users/{id}")
public UserEntity getUser(@PathVariable Long id) {
return userRepository.findById(id).orElseThrow();
}

Frage:

Warum ist das riskant?

Antwort:

Eine JPA Entity direkt zurückzugeben kann interne Felder preisgeben, sensible Daten leaken, Lazy Loading Probleme verursachen, endlose JSON Rekursion auslösen und die API eng an das Database Model koppeln. Gib stattdessen ein DTO zurück.


71. Kleine Bug-Übung 3

Problem:


@Controller
@RequestMapping("/api/hello")
public class HelloController {

@GetMapping
public String hello() {
return "hello";
}
}

Erwarteter Response Body:


hello

Aber Spring versucht, eine View aufzulösen.

Frage:

Warum?

Antwort:

@Controller enthält nicht automatisch @ResponseBody. Spring behandelt den zurückgegebenen String als View-Name. Nutze @RestController oder füge @ResponseBody hinzu.


Übungsfragen

Frage 1

Was ist ein HTTP Request Body?

Antwort:

Der HTTP Request Body ist die Datenmenge, die der Client an den Server sendet — oft JSON bei POST-, PUT- oder PATCH-Requests.


Frage 2

Was ist ein HTTP Response Body?

Antwort:

Der HTTP Response Body ist die Datenmenge, die der Server an den Client zurücksendet — oft JSON in REST APIs.


Frage 3

Was macht @RequestBody?

Antwort:

@RequestBody sagt Spring, den HTTP Request Body zu lesen und mit einem HttpMessageConverter in ein Java-Objekt zu konvertieren.


Frage 4

Was macht @ResponseBody?

Antwort:

@ResponseBody sagt Spring, den Methoden-Rückgabewert direkt in den HTTP Response Body zu schreiben — statt ihn als View-Name zu behandeln.


Frage 5

Was enthält @RestController?

Antwort:

@RestController enthält @Controller und @ResponseBody.


Frage 6

Was ist HttpMessageConverter?

Antwort:

HttpMessageConverter konvertiert zwischen HTTP Request/Response Bodies und Java-Objekten.


Frage 7

Was ist Jackson?

Antwort:

Jackson ist eine JSON Library, die Spring Boot nutzt, um Java-Objekte zu JSON und JSON zu Java-Objekten zu konvertieren.


Frage 8

Was ist Serialization?

Antwort:

Serialization bedeutet, ein Java-Objekt in JSON oder ein anderes externes Format zu konvertieren.


Frage 9

Was ist Deserialization?

Antwort:

Deserialization bedeutet, JSON oder ein anderes externes Format in ein Java-Objekt zu konvertieren.


Frage 10

Was ist ein DTO?

Antwort:

Ein DTO ist ein Data Transfer Object zum Transport von Daten über Layer oder API-Grenzen hinweg.


Frage 11

Was ist der Unterschied zwischen Request DTO und Response DTO?

Antwort:

Ein Request DTO beschreibt, was der Client an den Server sendet. Ein Response DTO beschreibt, was der Server an den Client zurückgibt.


Frage 12

Warum sollten REST APIs meist DTOs statt Entities zurückgeben?

Antwort:

DTOs vermeiden das Preisgeben der internen Database Structure, verhindern Leaks sensibler Felder, reduzieren Lazy Loading- und Endlos-Rekursions-Probleme, steuern die Response-Form und trennen den API Contract vom Persistence Model.


Frage 13

Wie validierst du einen Request Body?

Antwort:

Füge Validation Annotations zum Request DTO hinzu, füge spring-boot-starter-validation hinzu und nutze @Valid mit @RequestBody.


Frage 14

Was ist der Unterschied zwischen @NotNull, @NotEmpty und @NotBlank?

Antwort:

@NotNull bedeutet, der Wert darf nicht null sein. @NotEmpty bedeutet, er darf nicht null oder leer sein. @NotBlank bedeutet, ein String darf nicht null, leer oder nur Whitespace sein.


Frage 15

Was passiert, wenn Validation fehlschlägt?

Antwort:

Spring liefert üblicherweise 400 Bad Request.


Frage 16

Was steuert ResponseEntity?

Antwort:

ResponseEntity steuert HTTP Status Code, Headers und Body.


Frage 17

Wie gibst du 201 Created zurück?

Antwort:

Nutze:


return ResponseEntity.status(HttpStatus.CREATED).body(body);

oder:


return ResponseEntity.created(location).body(body);

Frage 18

Wie gibst du 204 No Content zurück?

Antwort:

Nutze:


return ResponseEntity.noContent().build();

Frage 19

Wann solltest du @ResponseStatus nutzen?

Antwort:

Nutze @ResponseStatus, wenn der Response Status fest und einfach ist.


Frage 20

Was ist der Unterschied zwischen ResponseEntity und @ResponseStatus?

Antwort:

ResponseEntity gibt flexible Kontrolle über Status, Headers und Body. @ResponseStatus setzt einen festen Status für eine Controller-Methode oder Exception.

Merksätze zum Mitnehmen

  • Der Request Body ist Daten vom Client zum Server.
  • Der Response Body ist Daten vom Server zum Client.
  • @RequestBody liest den Body in ein Java-Objekt.
  • @ResponseBody schreibt den Rückgabewert in den Response Body.
  • @RestController entspricht @Controller plus @ResponseBody.
  • HttpMessageConverter konvertiert zwischen HTTP Bodies und Java-Objekten.
  • Jackson konvertiert üblicherweise JSON zu Java und Java zu JSON.
  • Serialization bedeutet Java zu JSON.
  • Deserialization bedeutet JSON zu Java.
  • DTOs definieren den API Contract.
  • Request DTO und Response DTO können unterschiedlich sein.
  • REST APIs sollten meist DTOs statt Entities zurückgeben.
  • Füge spring-boot-starter-validation für Bean Validation hinzu.
  • Validation Annotations brauchen @Valid, um bei Request Bodies zu laufen.
  • @NotBlank ist am besten für Pflicht-Textfelder.
  • ResponseEntity steuert Status, Headers und Body.
  • 201 Created ist gut für Create-Operationen.
  • 204 No Content ist gut für erfolgreiches Delete ohne Body.
  • @ResponseStatus ist gut für einen einfachen festen Status.