Validação de Bean em Jersey
1. Visão geral
Neste tutorial, vamos dar uma olhada na Validação de Bean usando a estrutura de código abertoJersey.
Como já vimos em artigos anteriores,Jersey is an open source framework for developing RESTful Web Services. Podemos obter mais detalhes sobre Jersey em nossa introdução sobre comocreate an API with Jersey and Spring.
2. Validação de Bean em Jersey
Validation is the process of verifying that some data obeys one or more pre-defined constraints. É, é claro, um caso de uso muito comum na maioria dos aplicativos.
A estrutura Java BeanValidation (JSR-380) se tornou o padrão de fato para lidar com esse tipo de operação em Java. Para recapitular os fundamentos da Validação do Java Bean, consulte nossotutorial anterior.
Jersey contains an extension module to support Bean Validation. Para usar esse recurso em nosso aplicativo, primeiro precisamos configurá-lo. Na próxima seção, veremos como configurar nosso aplicativo.
3. Configuração do aplicativo
Agora, vamos construir o exemplo simples da API Fruit do excelente artigoJersey MVC Support.
3.1. Dependências do Maven
Em primeiro lugar, vamos adicionar a dependência Bean Validation ao nossopom.xml:
org.glassfish.jersey.ext
jersey-bean-validation
2.27
Podemos obter a versão mais recente deMaven Central.
3.2. Configurando o servidor
Em Jersey, normalmente registramos o recurso de extensão que queremos usar em nossa classe de configuração de recursos personalizados.
No entanto, para a extensão de validação de bean, não há necessidade de fazer esse registro. Fortunately, this is one of the few extensions that the Jersey framework registers automatically.
Finalmente, para enviar erros de validação para o clientewe’ll add a server property to our a custom resource configuration:
public ViewApplicationConfig() {
packages("com.example.jersey.server");
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
4. Validando Métodos de Recurso JAX-RS
Nesta seção, explicaremos duas maneiras diferentes de validar parâmetros de entrada usando anotações de restrição:
-
Usando restrições internas da API de Validação de Bean
-
Criando uma restrição e um validador personalizados
4.1. Usando anotações de restrição integradas
Vamos começar examinando as anotações de restrição incorporadas:
@POST
@Path("/create")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void createFruit(
@NotNull(message = "Fruit name must not be null") @FormParam("name") String name,
@NotNull(message = "Fruit colour must not be null") @FormParam("colour") String colour) {
Fruit fruit = new Fruit(name, colour);
SimpleStorageService.storeFruit(fruit);
}
Neste exemplo, criamos um novoFruit usando dois parâmetros de formulário,nameecolour. Usamos a anotação@NotNull que já faz parte da API Bean Validation.
Isso impõe uma restrição simples e não nula em nossos parâmetros de formulário. In case one of the parameters is null, the message declared within the annotation will be returned.
Naturalmente, vamos demonstrar isso com um teste de unidade:
@Test
public void givenCreateFruit_whenFormContainsNullParam_thenResponseCodeIsBadRequest() {
Form form = new Form();
form.param("name", "apple");
form.param("colour", null);
Response response = target("fruit/create").request(MediaType.APPLICATION_FORM_URLENCODED)
.post(Entity.form(form));
assertEquals("Http Response should be 400 ", 400, response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit colour must not be null"));
}
No exemplo acima,we use the JerseyTest support class to test our fruit resource. Enviamos uma solicitação POST comcoloure nulo e verificamos se a resposta contém a mensagem esperada.
Para obter uma lista de restrições de validação integradas, dê uma olhada emat the docs.
4.2. Definindo uma anotação de restrição personalizada
Às vezes, precisamos impor restrições mais complexas. We can do this by defining our own custom annotation.
Usando nosso exemplo simples de API de frutas, vamos imaginar que precisamos validar se todas as frutas têm um número de série válido:
@PUT
@Path("/update")
@Consumes("application/x-www-form-urlencoded")
public void updateFruit(@SerialNumber @FormParam("serial") String serial) {
//...
}
Neste exemplo, o parâmetroserial deve satisfazer as restrições definidas por@SerialNumber, que definiremos a seguir.
Definiremos primeiro a anotação de restrição:
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { SerialNumber.Validator.class })
public @interface SerialNumber {
String message()
default "Fruit serial number is not valid";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
A seguir, definiremos a classe validadoraSerialNumber.Validator:
public class Validator implements ConstraintValidator {
@Override
public void initialize(SerialNumber serial) {
}
@Override
public boolean isValid(String serial,
ConstraintValidatorContext constraintValidatorContext) {
String serialNumRegex = "^\\d{3}-\\d{3}-\\d{4}$";
return Pattern.matches(serialNumRegex, serial);
}
}
The key point here is the Validator class must implement ConstraintValidator where T is the type of value we want to validate, in our case a String.
Finally, we then implement our custom validation logic in the isValid method.
5. Validação de Recursos
Furthermore, the Bean Validation API also allows us to validate objects using the @Valid annotation.
Na próxima seção, explicaremos duas maneiras diferentes de validar classes de recursos usando esta anotação:
-
Primeiro, solicite validação de recurso
-
Segundo, validação de recurso de resposta
Vamos começar adicionando a anotação@Min ao nosso objetoFruit:
@XmlRootElement
public class Fruit {
@Min(value = 10, message = "Fruit weight must be 10 or greater")
private Integer weight;
//...
}
5.1. Solicitar validação de recurso
Primeiro de tudo, vamos habilitar a validação usando@Valid em nossa classeFruitResource:
@POST
@Path("/create")
@Consumes("application/json")
public void createFruit(@Valid Fruit fruit) {
SimpleStorageService.storeFruit(fruit);
}
No exemplo acima,if we try to create a fruit with a weight less than 10 we will get a validation error.
5.2. Validação de recursos de resposta
Da mesma forma, no próximo exemplo, veremos como validar um recurso de resposta:
@GET
@Valid
@Produces("application/json")
@Path("/search/{name}")
public Fruit findFruitByName(@PathParam("name") String name) {
return SimpleStorageService.findByName(name);
}
Observe como usamos a mesma anotação@Valid. But this time we use it at the resource method level to be sure the response is valid.
6. Manipulador de exceção personalizado
Nesta última parte, veremos brevemente como criar um manipulador de exceção personalizado. This is useful when we want to return a custom response if we violate a particular constraint.
Vamos começar definindo nossoFruitExceptionMapper:
public class FruitExceptionMapper implements ExceptionMapper {
@Override
public Response toResponse(ConstraintViolationException exception) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(prepareMessage(exception))
.type("text/plain")
.build();
}
private String prepareMessage(ConstraintViolationException exception) {
StringBuilder message = new StringBuilder();
for (ConstraintViolation> cv : exception.getConstraintViolations()) {
message.append(cv.getPropertyPath() + " " + cv.getMessage() + "\n");
}
return message.toString();
}
}
Primeiro, definimos um provedor de mapeamento de exceção personalizado. Para fazer isso, implementamos a interfaceExceptionMapper usando umConstraintViolationException.
Hence, we’ll see that when this exception is thrown the toResponse method of our custom exception mapper instance will be invoked.
Além disso, neste exemplo simples, iteramos todas as violações e anexamos cada propriedade e mensagem a ser enviada de volta na resposta.
Next, in order to use our custom exception mapper we need to register our provider:
@Override
protected Application configure() {
ViewApplicationConfig config = new ViewApplicationConfig();
config.register(FruitExceptionMapper.class);
return config;
}
Finalmente, adicionamos um endpoint para retornar umFruit inválido para mostrar o manipulador de exceção em ação:
@GET
@Produces(MediaType.TEXT_HTML)
@Path("/exception")
@Valid
public Fruit exception() {
Fruit fruit = new Fruit();
fruit.setName("a");
fruit.setColour("b");
return fruit;
}
7. Conclusão
Para resumir, neste tutorial, exploramos a extensão da API de validação de Jersey Bean.
Primeiro, começamos apresentando como a API de validação de feijão pode ser usada em Jersey. Além disso, vimos como configurar um aplicativo Web de exemplo.
Finalmente, vimos várias maneiras de validar com Jersey e como escrever um manipulador de exceção personalizado.
Como sempre, o código-fonte completo do artigo está disponívelover on GitHub.