--- title: Interceptadores --- #Quando interceptar? O uso de interceptadores é feito para executar alguma tarefa antes e/ou depois de uma lógica de negócios, sendo os usos mais comuns a validação de dados, controle de conexão e transação do banco, log e criptografia/compactação de dados. ##Como interceptar? No VRaptor utilizamos uma abordagem onde o interceptador define quem será interceptado, muito mais próxima a abordagens de interceptação que aparecem em sistemas baseados em AOP (Programação Orientada a Aspectos). Portanto, para interceptar uma requisição, basta anotar a classe com a anotação `@Intercepts`. Assim como qualquer outro componente, com o uso das anotações de escopo você pode dizer em que escopo o interceptador será armazenado. ##Exemplo simples A classe a seguir mostra um exemplo de como interceptar todas as requisições em um escopo de requisição e simplesmente mostrar na saída do console o que está sendo invocado. Lembre-se de que o interceptador é um componente como qualquer outro e pode receber quaisquer dependências por meio de Injeção de Dependências. ~~~ #!java @Intercepts @RequestScoped public class Log { @Inject private final HttpServletRequest request; @AroundCall public void intercept(SimpleInterceptorStack stack) { System.out.println("Interceptando " + request.getRequestURI()); // código a ser executado antes da lógica stack.next(); // continua a execução } } ~~~ Para executar um código apenas antes ou depois da execução do controller, podemos usar as anotações `@BeforeCall` e `@AfterCall`: ~~~ #!java @Intercepts @RequestScoped public class Log { @BeforeCall public void before() { // código a ser executado antes da lógica } @AfterCall public void after() { // código a ser executado depois da lógica } } ~~~ Repare que não é mais obrigatório implementar a interface `Interceptor`, basta criar a classe e usar as anotações acima. ##Definindo quando interceptar No exemplo acima todas as requisições são interceptadas. Podemos então definir que iremos interceptar apenas os métodos que possuem uma anotação chamada `@Audit`. Para isso basta implementar um método que retorne um `boolean`com a condição necessária e anotá-lo com `@Accepts`. ~~~ #!java @Accepts public boolean accepts(ControllerMethod method) { return method.containsAnnotation(Audit.class); } ~~~ No VRaptor4 também temos outra maneira de definir a condição de aceitação. O exemplo acima é um caso bem comum e acabamos tendo de repetir esse código muitas vezes. Para essas situações você pode usar a annotation `@AcceptsWithAnnotations`. ~~~ #!java @AcceptsWithAnnotations(Audit.class) public class AuditInterceptor { @AroundCall public void around(SimpleInterceptorStack stack){ stack.next(); } } ~~~ ##Criando o seu Accepts customizado Logo acima usamos o exemplo do Accepts customizado. Este é um que já vem pronto VRaptor. Mas nada impede de você criar o seu para as suas necessidades. O fluxo é bem parecido com o de criação de uma validação customizada para a BeanValidation. Primeiro você deve criar sua Annotation. ~~~ #!java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @AcceptsConstraint(WithAnnotationAcceptor.class) @Inherited public @interface AcceptsWithAnnotations { public Class[] value(); } ~~~ Perceba que usamos a Annotation `@AcceptsConstraint` para indicar qual a classe responsável por fazer a verificação. Agora precisamos implementar essa classe. ~~~ #!java public class WithAnnotationAcceptor implements AcceptsValidator { @Override public boolean validate(ControllerMethod controllerMethod, ControllerInstance instance) { //aqui entra sua logica de validação do método ou instancia do controller. return true; } @Override public void initialize(AcceptsWithAnnotations annotation) { //aqui você pode acessar as informações contidas na annotation customizada. this.allowedTypes = Arrays.asList(annotation.value()); } } ~~~ Esta classe só precisa implemetar a interface `AcceptsValidator` onde T é o tipo da sua Annotation customizada. Dessa maneira você pode criar as suas condições customizadas e disponibilizá-las para todos os projetos que você precisa. ##Como garantir ordem: after e before Se é preciso garantir a ordem de execução de um conjunto de interceptors, você pode usar os atributos `after` e `before` da anotação `@Intercepts`. Suponha que o `PrimeiroInterceptor` tenha que rodar antes do `SegundoInterceptor`, então você pode configurar isso tanto com: ~~~ #!java @Intercepts(before=SegundoInterceptor.class) public class PrimeiroInterceptor { ... } ~~~ quanto com: ~~~ #!java @Intercepts(after=PrimeiroInterceptor.class) public class SegundoInterceptor { ... } ~~~ Você pode especificar mais de um Interceptor: ~~~ #!java @Intercepts(after={PrimeiroInterceptor.class, SegundoInterceptor.class}, before={QuartoInterceptor.class, QuintoInterceptor.class}) public class TerceiroInterceptor { ... } ~~~ O VRaptor lançará uma exception se existir um ciclo na ordem dos interceptors, então tenha cuidado.