Woche 4, Tag 3 — Request- und Response-Bodies
Ziel
Heute verstehst du, wie Spring MVC Request Bodies und Response Bodies verarbeitet.
Die Kernfragen:
- Was ist ein HTTP Request Body?
- Was ist ein HTTP Response Body?
- Was macht
@RequestBody? - Was macht
@ResponseBody? - Warum liefert
@RestControllerautomatisch JSON zurück? - Was ist
HttpMessageConverter? - Was ist Jackson?
- Was sind DTOs?
- Warum sollten REST APIs meist DTOs statt Entities zurückgeben?
- Wie funktioniert Validation mit
@Valid? - Was ist
ResponseEntity? - Welche typischen Prüfungsfallen gibt es?
1. Kurz-Wiederholung von Woche 4, Tag 2
An Tag 2 hast du gelernt:
@RequestMappingmappt HTTP Requests auf Controller-Methoden.@GetMapping,@PostMapping,@PutMapping,@PatchMappingund@DeleteMappingsind Shortcut-Annotations.@PathVariableliest Werte aus dem Path.@RequestParamliest Query Parameters.@RequestHeaderliest Headers.@RequestBodyliest den HTTP Body.consumesprüft den RequestContent-Type.producesbeschreibt den Response Media Type.Content-Typebedeutet: Format des Request Bodies.Acceptbedeutet: 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:
@RequestBodybedeutet: 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-Typesagt 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:
HttpMessageConverterkonvertiert 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:
| Annotation | Bedeutung |
|---|---|
@NotNull | Wert darf nicht null sein |
@NotBlank | String muss Text ohne nur Whitespace enthalten |
@NotEmpty | Collection/String darf nicht leer sein |
@Size | Größe muss im Bereich liegen |
@Min | Zahl muss mindestens den Wert haben |
@Max | Zahl darf höchstens den Wert haben |
@Email | String muss Email-Format haben |
@Positive | Zahl 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.
@Validtriggert 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
@Validauch 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
@Validmit@RequestBody, um Request DTOs zu validieren.@Validatedist 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:
ResponseEntitygibt 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
| Thema | ResponseEntity | @ResponseStatus |
|---|---|---|
| Status | dynamisch oder fest | fest |
| Headers | einfach steuerbar | nicht ideal |
| Body | explizit | Methoden-Rückgabewert |
| Häufiger Einsatz | flexible Responses | einfacher fester Status |
| Beispiel | 200 oder 404 | immer 201 |
Merksatz:
Nutze
ResponseEntityfür Kontrolle. Nutze@ResponseStatusfü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
@Validhinzu, 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
| Situation | Status |
|---|---|
| Erfolgreicher GET mit Body | 200 OK |
| Erfolgreiches Create | 201 Created |
| Erfolgreiches Delete ohne Body | 204 No Content |
| Validation Error | 400 Bad Request |
| Ungültiges JSON | 400 Bad Request |
| Resource nicht gefunden | 404 Not Found |
| Nicht unterstützter Request Body Type | 415 Unsupported Media Type |
| Nicht unterstützter Response Type angefordert | 406 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
ResponseEntityused 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:
- Was macht
@RequestBody? - Was macht
@Valid? - Welcher Status Code wird zurückgegeben?
- Welcher Header wird zurückgegeben?
- Wer konvertiert
TaskDtozu JSON?
Antworten:
- Es liest den JSON Request Body in
CreateTaskRequest. - Es triggert Validation.
201 Created.Location: /api/tasks/1.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.
@RequestBodyliest den Body in ein Java-Objekt.@ResponseBodyschreibt den Rückgabewert in den Response Body.@RestControllerentspricht@Controllerplus@ResponseBody.HttpMessageConverterkonvertiert 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-validationfür Bean Validation hinzu. - Validation Annotations brauchen
@Valid, um bei Request Bodies zu laufen. @NotBlankist am besten für Pflicht-Textfelder.ResponseEntitysteuert Status, Headers und Body.201 Createdist gut für Create-Operationen.204 No Contentist gut für erfolgreiches Delete ohne Body.@ResponseStatusist gut für einen einfachen festen Status.