Validação no Spring Boot
1. Visão geral
Quando se trata de validar a entrada do usuário,Spring Boot fornece um forte suporte para essa tarefa comum, mas crítica, pronta para uso.
Embora o Spring Boot suporte integração perfeita com validadores personalizados,the de-facto standard for performing validation is Hibernate Validator, a implementação de referênciaBean Validation framework's.
Neste tutorial,we’ll look at how to validate domain objects in Spring Boot.
Leitura adicional:
Validação personalizada MessageSource na inicialização por mola
Aprenda a registrar um MessageSource personalizado para mensagens de validação no Spring Boot.
Diferença entre as restrições @NotNull, @NotEmpty e @NotBlank na validação de bean
Aprenda a semântica das anotações de validação de feijão @NotNull, @NotEmpty e @NotBlank em Java e como elas diferem.
2. As dependências do Maven
Neste caso, aprenderemos como validar objetos de domínio em Spring Bootby building a basic REST controller.
O controlador primeiro pega um objeto de domínio, depois o valida com o Hibernate Validator e, por fim, o mantém em um banco de dados H2 na memória.
As dependências do projeto são razoavelmente padronizadas:
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
com.h2database
h2
1.4.197
runtime
Conforme mostrado acima, incluímosspring-boot-starter-web no arquivoour pom.xml, porque vamos precisar dele para criar o controlador REST. Além disso, vamos verificar as versões mais recentes despring-boot-starter-jpa eH2 database no Maven Central.
3. Uma classe de domínio simples
Com as dependências do nosso projeto já implementadas, em seguida, precisamos definir uma classe de entidade JPA de exemplo, cuja função será apenas modelar usuários.
Vamos dar uma olhada nesta aula:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotBlank(message = "Name is mandatory")
private String name;
@NotBlank(message = "Email is mandatory")
private String email;
// standard constructors / setters / getters / toString
}
A implementação de nossa classe de entidadeUser é bastante anêmica. Mas mostra em poucas palavras como usar as restrições do Bean Validation para restringir os camposnameeemail.
Para simplificar, restringimos os campos de destino usando apenas a restrição@NotBlank. Além disso, especificamos as mensagens de erro com o atributomessage.
Portanto, quando o Spring Boot valida a instância da classe, os campos restritosmust be not null and their trimmed length must be greater than zero.
Além disso,Bean Validation fornece muitas outras restrições úteis além de@NotBlank.. Isso nos permite aplicar e combinar diferentes regras de validação para as classes restritas. Para obter mais informações, leiaofficial bean validation docs.
Como usaremosSpring Data JPA para salvar usuários no banco de dados H2 na memória, também precisamos definir uma interface de repositório simples para ter a funcionalidade CRUD básica em objetosUser:
@Repository
public interface UserRepository extends CrudRepository {}
4. Implementando um controlador REST
Claro, precisamos implementar uma camada que nos permite obter os valores atribuídos aos campos restritos do nosso objetoUser.
Portanto, podemos validá-los e executar algumas tarefas adicionais, dependendo dos resultados da validação.
Spring Boot fazall this seemingly-complex process really simple através da implementação de um controlador REST.
Vejamos a implementação do controlador REST:
@RestController
public class UserController {
@PostMapping("/users")
ResponseEntity addUser(@Valid @RequestBody User user) {
// persisting the user
return ResponseEntity.ok("User is valid");
}
// standard constructors / other methods
}
Em aSpring REST context, a implementação do métodoaddUser() é razoavelmente padrão.
Claro, a parte mais relevante é o uso da anotação@Valid.
Quando Spring Boot encontra um argumento anotado com@Valid, ele inicializa automaticamente a implementação JSR 380 padrão - Hibernate Validator - e valida o argumento.
Quando o argumento de destino falha em passar na validação, Spring Boot lança uma exceçãoMethodArgumentNotValidException.
5. A anotação@ExceptionHandler
Embora seja realmente útil ter Spring Boot validando automaticamente o objetoUser passado para o métodoaddUser(), a faceta que falta neste processo é como processamos os resultados da validação.
A anotação@ExceptionHandlerallows us to handle specified types of exceptions through one single method.
Portanto, podemos usá-lo para processar os erros de validação:
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
Especificamos a exceçãoMethodArgumentNotValidException comothe exception to be handled. Em conseqüência, Spring Boot irá chamar este métodowhen the specified User object is invalid.
O método armazena o nome e a mensagem de erro pós-validação de cada campo inválido em umMap.. Em seguida, ele enviaMap de volta ao cliente como uma representação JSON para processamento posterior.
Simplificando, o controlador REST nos permite processar facilmente solicitações para diferentes terminais, validar objetosUser e enviar as respostas no formato JSON.
6. Testando o controlador REST
Podemos testar facilmente a funcionalidade de nosso controlador REST com umintegration test.
Vamos começar a simular / autowiring a implementação da interfaceUserRepository, junto com a instânciaUserController e um objetoMockMvc:
@RunWith(SpringRunner.class)
@WebMvcTest
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {
@MockBean
private UserRepository userRepository;
@Autowired
UserController userController;
@Autowired
private MockMvc mockMvc;
//...
}
Como estamos apenas testando a camada da web, usamos a anotação@WebMvcTest. Ele nos permite testar facilmente solicitações e respostas usando o conjunto de métodos estáticos implementados pelas classesMockMvcRequestBuilderseMockMvcResultMatchers.
Agora, vamos testar o métodoaddUser(), com um objetoUser válido e um inválido passado no corpo da solicitação:
@Test
public void whenPostRequestToUsersAndValidUser_thenCorrectResponse() throws Exception {
MediaType textPlainUtf8 = new MediaType(MediaType.TEXT_PLAIN, Charset.forName("UTF-8"));
String user = "{\"name\": \"bob\", \"email\" : \"[email protected]\"}";
mockMvc.perform(MockMvcRequestBuilders.post("/users")
.content(user)
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content()
.contentType(textPlainUtf8));
}
@Test
public void whenPostRequestToUsersAndInValidUser_thenCorrectResponse() throws Exception {
String user = "{\"name\": \"\", \"email\" : \"[email protected]\"}";
mockMvc.perform(MockMvcRequestBuilders.post("/users")
.content(user)
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(MockMvcResultMatchers.status().isBadRequest())
.andExpect(MockMvcResultMatchers.jsonPath("$.name", Is.is("Name is mandatory")))
.andExpect(MockMvcResultMatchers.content()
.contentType(MediaType.APPLICATION_JSON_UTF8));
}
}
Além disso, podemos testar a API do controlador RESTusing a free API lifecycle testing application, comoPostman ouKatalon Studio.
7. Executando o aplicativo de amostra
Finalmente, podemos executar nosso projeto de exemplo com um métodomain() padrão:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner run(UserRepository userRepository) throws Exception {
return (String[] args) -> {
User user1 = new User("Bob", "[email protected]");
User user2 = new User("Jenny", "[email protected]");
userRepository.save(user1);
userRepository.save(user2);
userRepository.findAll().forEach(System.out::println);
};
}
}
Como esperado, devemos ver alguns objetosUser impressos no console.
Uma solicitação POST para o endpointhttp://localhost:8080/users com um objetoUser válido retornará oString “O usuário é válido”.
Da mesma forma, uma solicitação POST com um objetoUser sem os valoresnameeemail retornará a seguinte resposta:
{
"name":"Name is mandatory",
"email":"Email is mandatory"
}
8. Conclusão
Neste tutorial,we learned the basics of performing validation in Spring Boot.
Como de costume, todos os exemplos mostrados neste tutorial estão disponíveis emGitHub.