diff --git a/http-requests/POST-blogposts.http b/http-requests/POST-blogposts.http
index 9c8703c..be58461 100644
--- a/http-requests/POST-blogposts.http
+++ b/http-requests/POST-blogposts.http
@@ -12,3 +12,19 @@ Content-Type: application/json
client.assert(response.status === 201);
});
%}
+
+###
+
+POST {{endpoint}}/blogposts
+Accept: application/json
+Content-Type: application/json
+
+{
+ "content": "Das ist ein Test"
+}
+
+> {%
+ client.test("BlogPosts returned", () => {
+ client.assert(response.status === 400);
+ });
+%}
diff --git a/pom.xml b/pom.xml
index 02c2c38..ddba5a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,6 +30,10 @@
org.springframework.boot
spring-boot-starter-hateoas
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
org.springframework.boot
diff --git a/src/main/java/de/sample/schulung/spring/blog/boundary/GlobalExceptionHandler.java b/src/main/java/de/sample/schulung/spring/blog/boundary/GlobalExceptionHandler.java
new file mode 100644
index 0000000..5ed59f5
--- /dev/null
+++ b/src/main/java/de/sample/schulung/spring/blog/boundary/GlobalExceptionHandler.java
@@ -0,0 +1,19 @@
+package de.sample.schulung.spring.blog.boundary;
+
+import jakarta.validation.ConstraintViolationException;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(ConstraintViolationException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ void handleValidationException() {
+ // do nothing
+ // return ResponseEntity, Fehler-Object (JSON), ProblemDetail
+ }
+
+}
diff --git a/src/main/java/de/sample/schulung/spring/blog/domain/BlogPost.java b/src/main/java/de/sample/schulung/spring/blog/domain/BlogPost.java
index 30b0c4b..0469cc8 100644
--- a/src/main/java/de/sample/schulung/spring/blog/domain/BlogPost.java
+++ b/src/main/java/de/sample/schulung/spring/blog/domain/BlogPost.java
@@ -1,5 +1,7 @@
package de.sample.schulung.spring.blog.domain;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@@ -13,7 +15,11 @@
public class BlogPost {
private UUID id;
+ @Size(min = 3)
+ @NotNull
private String title;
+ @Size(min = 10)
+ @NotNull
private String content;
private LocalDateTime timestamp;
diff --git a/src/main/java/de/sample/schulung/spring/blog/domain/BlogPostService.java b/src/main/java/de/sample/schulung/spring/blog/domain/BlogPostService.java
index 29bc422..a595527 100644
--- a/src/main/java/de/sample/schulung/spring/blog/domain/BlogPostService.java
+++ b/src/main/java/de/sample/schulung/spring/blog/domain/BlogPostService.java
@@ -1,6 +1,9 @@
package de.sample.schulung.spring.blog.domain;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.util.HashMap;
@@ -9,6 +12,7 @@
import java.util.UUID;
import java.util.stream.Stream;
+@Validated
@Service
public class BlogPostService {
@@ -24,7 +28,7 @@ public Optional findById(UUID id) {
);
}
- public void create(BlogPost blogPost) {
+ public void create(@Valid @NotNull BlogPost blogPost) {
final var id = UUID.randomUUID();
blogPost.setId(id);
blogPost.setTimestamp(LocalDateTime.now());
diff --git a/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiTests.java b/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiTests.java
index a6f2a1b..d6a8fa1 100644
--- a/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiTests.java
+++ b/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiTests.java
@@ -62,6 +62,22 @@ void shouldCreateBlogPostSuccessfully() throws Exception {
}
+ @Test
+ void shouldNotCreateInvalidBlogPost() throws Exception {
+ mvc.perform(
+ post("/blogposts")
+ .accept(MediaType.APPLICATION_JSON)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("""
+ {
+ "content": "Das ist ein Test"
+ }
+ """)
+ )
+ .andExpect(status().isBadRequest());
+
+ }
+
/*
* Testfall:
* - Lege BlogPost an (title: "test")
diff --git a/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiWithMockedServiceTests.java b/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiWithMockedServiceTests.java
index 836d14e..651cbbc 100644
--- a/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiWithMockedServiceTests.java
+++ b/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiWithMockedServiceTests.java
@@ -14,8 +14,10 @@
import java.util.Optional;
import java.util.UUID;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -58,6 +60,23 @@ void shouldReturnSingleBlogPostSuccessfully() throws Exception {
.andExpect(jsonPath("$.timestamp").exists());
}
+ @Test
+ void shouldNotCreateInvalidBlogPost() throws Exception {
+ mvc.perform(
+ post("/blogposts")
+ .accept(MediaType.APPLICATION_JSON)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("""
+ {
+ "content": "Das ist ein Test"
+ }
+ """)
+ )
+ .andExpect(status().isBadRequest());
+ verifyNoInteractions(service);
+
+ }
+
// TODO wenn ID im Request mitgeschickt wird -> ??