BAEL-3005 | Validating List objects in Spring
This commit is contained in:
parent
a057974b11
commit
a178f09ef4
|
@ -18,6 +18,10 @@
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.baeldung.validation.listvalidation;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@ComponentScan(basePackages = "com.baeldung.validation.listvalidation")
|
||||||
|
@Configuration
|
||||||
|
@SpringBootApplication
|
||||||
|
public class SpringListValidationApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(SpringListValidationApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.baeldung.validation.listvalidation.constraint;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
|
||||||
|
@Constraint(validatedBy = CustomConstraintValidator.class)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface CustomConstraint {
|
||||||
|
|
||||||
|
String message() default "Invalid movie name.";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.baeldung.validation.listvalidation.constraint;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
|
||||||
|
public class CustomConstraintValidator implements ConstraintValidator<CustomConstraint, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.baeldung.validation.listvalidation.controller;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
|
import com.baeldung.validation.listvalidation.model.Movie;
|
||||||
|
import com.baeldung.validation.listvalidation.repository.MovieRepository;
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/movie")
|
||||||
|
public class MovieController {
|
||||||
|
|
||||||
|
private final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MovieRepository movieRepository;
|
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.POST)
|
||||||
|
public void add(@RequestBody @Valid Movie movie, BindingResult bindingResult) {
|
||||||
|
if (bindingResult.hasErrors()) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
bindingResult.getAllErrors()
|
||||||
|
.forEach(error -> builder.append(" " + error.getDefaultMessage()));
|
||||||
|
LOGGER.error(builder.toString());
|
||||||
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, bindingResult.toString());
|
||||||
|
}
|
||||||
|
movieRepository.add(movie);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/batch", method = RequestMethod.POST)
|
||||||
|
public void addAll(@RequestBody List<@Valid Movie> movie) {
|
||||||
|
movieRepository.addAll(movie);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.GET, value = "/{name}")
|
||||||
|
public Movie get(@PathVariable String name) throws Exception {
|
||||||
|
return movieRepository.get(name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.baeldung.validation.listvalidation.exception;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.ConstraintViolationException;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class ConstraintViolationExceptionHandler {
|
||||||
|
private final Logger LOGGER = LoggerFactory.getLogger(ConstraintViolationExceptionHandler.class);
|
||||||
|
|
||||||
|
@ExceptionHandler(ConstraintViolationException.class)
|
||||||
|
public ResponseEntity<String> handle(ConstraintViolationException constraintViolationException) {
|
||||||
|
Set<ConstraintViolation<?>> violations = constraintViolationException.getConstraintViolations();
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (ConstraintViolation<?> violation : violations) {
|
||||||
|
builder.append(violation.getMessage());
|
||||||
|
|
||||||
|
}
|
||||||
|
LOGGER.error(builder.toString());
|
||||||
|
return new ResponseEntity<String>(builder.toString(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.baeldung.validation.listvalidation.model;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Actor {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Actor(String name) {
|
||||||
|
this.id = UUID.randomUUID()
|
||||||
|
.toString();
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.baeldung.validation.listvalidation.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
import com.baeldung.validation.listvalidation.constraint.CustomConstraint;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Movie {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@CustomConstraint
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Size(min = 2, message = "Atleast 2 genres should be provided.")
|
||||||
|
private List<String> genres;
|
||||||
|
|
||||||
|
@NotNull(message = "Actor list cannot be null.")
|
||||||
|
@NotEmpty(message = "Actor list cannot be empty.")
|
||||||
|
private List<Actor> actors;
|
||||||
|
|
||||||
|
public Movie(String name, List<String> genres, List<Actor> actors) {
|
||||||
|
this.id = UUID.randomUUID()
|
||||||
|
.toString();
|
||||||
|
this.name = name;
|
||||||
|
this.genres = genres;
|
||||||
|
this.actors = actors;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.baeldung.validation.listvalidation.repository;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import com.baeldung.validation.listvalidation.model.Actor;
|
||||||
|
import com.baeldung.validation.listvalidation.model.Movie;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public class MovieRepository {
|
||||||
|
private final Logger LOGGER = LoggerFactory.getLogger(MovieRepository.class);
|
||||||
|
static List<Movie> moviesData;
|
||||||
|
|
||||||
|
static {
|
||||||
|
moviesData = new ArrayList<>();
|
||||||
|
|
||||||
|
Actor a1 = new Actor("Actor1");
|
||||||
|
|
||||||
|
Movie m1 = new Movie("MovieABC", Arrays.asList("Drama"), Arrays.asList(a1));
|
||||||
|
moviesData.add(m1);
|
||||||
|
|
||||||
|
Movie m2 = new Movie("MovieDEF", Arrays.asList("Drama"), Arrays.asList(a1));
|
||||||
|
moviesData.add(m2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Movie movie) {
|
||||||
|
if (get(movie.getName()) == null) {
|
||||||
|
moviesData.add(movie);
|
||||||
|
LOGGER.info("Added new movie.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Movie get(String name) {
|
||||||
|
Movie movie = null;
|
||||||
|
for (Movie m : moviesData) {
|
||||||
|
if (name.equalsIgnoreCase(m.getName())) {
|
||||||
|
movie = m;
|
||||||
|
LOGGER.info("Found movie with name " + name + ":" + movie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return movie;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAll(List<Movie> movies) {
|
||||||
|
for (Movie movie : movies) {
|
||||||
|
add(movie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package com.baeldung.validation.listvalidation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||||
|
|
||||||
|
import com.baeldung.validation.listvalidation.model.Actor;
|
||||||
|
import com.baeldung.validation.listvalidation.model.Movie;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest(classes = SpringListValidationApplication.class)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
public class MovieControllerIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MockMvc mvc;
|
||||||
|
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void given1Genre_whenAddingMovie_thenThrowBadRequest() throws Exception {
|
||||||
|
Actor actor = new Actor("Actor1");
|
||||||
|
Movie movie = new Movie("Movie1", Arrays.asList("Drama"), Arrays.asList(actor));
|
||||||
|
mvc.perform(MockMvcRequestBuilders.post("/movie")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||||
|
.content(objectMapper.writeValueAsString(movie)))
|
||||||
|
.andExpect(MockMvcResultMatchers.status()
|
||||||
|
.isBadRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenWithoutActor_whenAddingMovieList_thenThrowBadRequest() throws Exception {
|
||||||
|
List<Actor> actors = new ArrayList<>();
|
||||||
|
Movie movie = new Movie("Movie2", Arrays.asList("Action", "Thriller"), actors);
|
||||||
|
mvc.perform(MockMvcRequestBuilders.post("/movie/batch")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||||
|
.content(objectMapper.writeValueAsString(Arrays.asList(movie))))
|
||||||
|
.andExpect(MockMvcResultMatchers.status()
|
||||||
|
.isBadRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenEmptyMovieName_whenAddingMovie_thenThrowBadRequest() throws Exception {
|
||||||
|
Actor actor = new Actor("Actor1");
|
||||||
|
Movie movie = new Movie("", Arrays.asList("Drama", "History"), Arrays.asList(actor));
|
||||||
|
mvc.perform(MockMvcRequestBuilders.post("/movie")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||||
|
.content(objectMapper.writeValueAsString(movie)))
|
||||||
|
.andExpect(MockMvcResultMatchers.status()
|
||||||
|
.isBadRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue