--- title: Validação --- # Validação A validação do VRaptor tira proveito do Bean Validation, especificação presente no Java EE 7 que permite validar nosso modelo baseado em anotações. Com o Bean Validation você pode usar as validações já existentes na especificação ou criar suas próprias anotações. ~~~ #!java public class Cliente { // valida se o nome não é nulo e possui tamanho entre 10 e 50 @NotNull @Size(min=10, max=50) private String nome; // valida se a data de nascimento está no passado @Past private Date nascimento; } ~~~ Em seu controller: ~~~ #!java public class ClienteController { private final Validator validation; /** * @deprecated CDI eyes only */ protected ClienteController() { this(null); } @Inject public ClienteController(Validator validation) { this.validation = validation; } public void formulario() { } public void cadastrar(@NotNull @Valid Cliente cliente) { // em caso de erros irá redirecionar para a página de formulário validation.onErrorForwardTo(this).formulario(); } } ~~~ **Nota**: Se sua aplicação usa Hibernate ou JPA e você tem seu modelo anotado com as anotações do Bean Validation, você precisará chamar os métodos `Session.flush()` ou `EntityManager.flush()` para disparar a validação do Bean Validation antes de fazer qualquer `redirect` ou `forward`. O método `flush()` é responsável por sincronizar as alterações no estado dos objetos com o banco de dados. Sendo assim as validações do Bean Validation em entidates só serão executadas no `flush()`. ##Criando validações customizadas Com o Bean Validator você pode criar anotações com suas validações customizadas. Se você quer, por exemplo, validar se o telefone está no formato `(00) 0000-0000`, você pode criar uma anotação assim: ~~~ #!java @Documented @Constraint(validatedBy = {}) @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) @ReportAsSingleViolation @Pattern(regexp = "\\((\\d{2})\\) (\\d{4})-(\\d{4})") public @interface PhoneNumber { String message() default "{br.com.caelum.vraptor.validations.phonenumber.message}"; Class[] groups() default {}; Class[] payload() default {}; } ~~~ O valor da mensagem com `{}` será buscado do `ResourceBundle`. Ou você pode colocar uma mensagem fixa, como por exemplo: ~~~ #!java String message() default "Invalid number"; ~~~ Você pode também criar validações mais complexas que fazem acesso ao banco de dados, ou algum componente existente. Se você quiser validar se o login de um usuário já está em uso, basta criar o código abaixo: ~~~ #!java @Target({ ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = { LoginAvailableValidator.class }) @Documented public @interface LoginAvailable { String message() default "{login_already_exists}"; Class[] groups() default {}; Class[] payload() default {}; } ~~~ E agora o `LoginAvailableValidator` que fará a validação: ~~~ #!java public class LoginAvailableValidator implements ConstraintValidator { @Inject private UserDao userDao; public boolean isValid(User user, ConstraintValidatorContext context) { return !userDao.containsUserWithLogin(user.getLogin()); } } ~~~ Note no código que é possível injetar qualquer componente gerenciável pelo CDI, que pode ser uma DAO ou qualquer outro componente. ##Customizando as Mensagens Se você preferir é possível customizar as mensagens do Bean Validation. Para isso, basta adicionar o arquivo `ValidationMessages.properties` no seu classpath. Se você usa Maven o local é `/src/main/resources`. Considerando o exemplo anterior, o arquivo poderia ter a seguinte estrutura para customizar as mensagens padrões: ~~~ #!properties cliente.nome.vazio = O nome do cliente não pode ser vazio cliente.nome.tamanho = O nome do cliente deve possuir entre {min} e {max} caracteres cliente.nascimento.data = A data de nascimento ${validatedValue} informada deve ser no passado ~~~ E a classe teria que ser alterada para incluir essas chaves ~~~ #!java public class Cliente { @NotNull(message="{cliente.nome.vazio}") @Size(min=10, max=50, message="{cliente.nome.tamanho}") private String nome; @Past(message="{cliente.nascimento.data}") private Date nascimento; } ~~~ Observe que o valor contido em `message={}`, nas anotações indica que a mensagem vem do `ResourceBundle`. Veja que foi adicionado parâmetros afim de deixar nossas mensagens mais dinâmicas e reaproveitáveis. As possibilidades atualmente são: * os valores dos atributos da restrição mapeados com os nomes de atributo. Ex. `{min}` e `{max}` no caso de `@Size` * o atual valor a ser validado sob o nome de `${validatedValue}` * uso de if-else curto, como por exemplo: `${value > 1 ? 's' : ''}` * um bean mapeado com o nome de `formatter` expondo método var-arg `format(String format, Object... args)` o qual se comporta como `java.util.Formatter.format(String format, Object... args)`. Ex.: `${formatter.format('%1$.2f', validatedValue)}` ## Validação usando o Validator Caso você não queira usar o Bean Validation, você pode usar os métodos de validação do próprio `Validator` do VRaptor. O método mais simples é o `add`: ~~~ #!java if (cliente.getNome() == null) { //mensagem simples validator.add(new SimpleMessage("nome", "O nome deve ser preenchido")); //mensagem internacionalizada validator.add(new I18nMessage("nome", "nome.deve.ser.preenchido")); } ~~~ Podemos também usar os métodos `addIf` que adiciona a mensagem se a condição for verdadeira e o `ensure` que adiciona se a condição for falsa: ~~~ #!java validator.addIf(cliente.getNome() == null, new SimpleMessage("nome", "O nome deve ser preenchido")); validator.ensure(cliente.getNome() != null, new SimpleMessage("nome", "O nome deve ser preenchido")); ~~~ ##Redirecionando em caso de erro Quando ocorre um erro de validação, você pode redirecionar o usuário para qualquer outra tela. Abaixo alguns exemplos: ~~~ #!java validator.onErrorForwardTo(MusicController.class).list() ==> validator.onErrorUse(logic()).forwardTo(MusicController.class).list(); validator.onErrorRedirectTo(MusicController.class).list() ==> validator.onErrorUse(logic()).redirectTo(MusicController.class).list(); validator.onErrorUsePageOf(MusicController.class).list() ==> validator.onErrorUse(page()).of(MusicController.class).list(); validator.onErrorSendBadRequest() ==> validator.onErrorUse(status()).badRequest(errors); ~~~ Além disso, se o redirecionamento é para um método do mesmo controller, podemos usar: ~~~ #!java validator.onErrorForwardTo(this).list() ==> validator.onErrorUse(logic()).forwardTo(this.getClass()).list(); validator.onErrorRedirectTo(this).list() ==> validator.onErrorUse(logic()).redirectTo(this.getClass()).list(); validator.onErrorUsePageOf(this).list() ==> validator.onErrorUse(page()).of(this.getClass()).list(); ~~~ ##Mostrando os erros de validação no JSP Quando existem erros de validação, o VRaptor coloca um atributo na requisição chamado `errors` contendo a lista de erros. Essa lista é representada por items chave-valor, onde temos: - `category`: representa o caminho do atributo que originou o erro, cujo valor é uma convenção para `objeto.atributo` - `message`: representa a mensagem padrão da API do Bean Validation, normalmente um sufixo como: "deve estar no futuro", "não pode ser nulo", etc. E na view você pode imprimir: ~~~ #!jsp ${error.category} - ${error.message}
~~~ Você também pode buscar por um erro somente de uma determinada categoria, que é muito útil para quando você quer exibir os erros de validação ao lado de cada campo. ~~~ #!jsp ${errors.from('cliente.nome')} ~~~ O código acima irá imprimir "não pode ser nulo, não pode ser menor que 50". Você também pode usar o método `join` caso preferir exibir as mensagens usando outro separador, como o exemplo abaixo: ~~~ #!jsp ${errors.from('cliente.nome').join(' - ')} ~~~ Que irá imprimir: "não pode ser nulo - não pode ser menor que 50". ##Adicionando outras mensagens Quando você adiciona uma mensagem, por padrão o VRaptor adiciona a mensagem como um erro. É possível também que você adicione mensagem de sucesso, avisos e informações. Para isso basta criar sua mensagem usando os valores disponíveis no enum `Severity`, conforme os exemplos abaixo: ~~~ #!java validator.add(new SimpleMessage("cliente.nome", "nome do cliente possui acentuação", Severity.WARN)); ~~~ E no JSP basta usar o atributo `vmessages`: ~~~ #!jsp ${vmessages.warnings.from('cliente.nome')} ~~~ Que irá imprimir "nome do cliente possui acentuação".