Introdução ao Spring MVC HandlerInterceptor
1. Introdução
Neste tutorial, vamos nos concentrar em entender o Spring MVCHandlerInterceptor e como usá-lo corretamente. __
2. Spring MVC Handler
E para entender o interceptor, vamos dar um passo para trás e olhar para oHandlerMapping. Isso mapeia um método para uma URL, de forma queDispatcherServlet possa invocá-lo ao processar uma solicitação.
E oDispatcherServlet usaHandlerAdapter para realmente invocar o método.
Agora que entendemos o contexto geral -this is where the handler interceptor comes in. UsaremosHandlerInterceptor para executar ações antes de manipular, após manipular ou após a conclusão (quando a visualização é renderizada) de uma solicitação.
O interceptador pode ser usado para preocupações transversais e para evitar códigos repetitivos de manipulador, como: registro, alteração de parâmetros usados globalmente no modelo Spring etc.
Nas próximas seções, é exatamente isso que veremos - as diferenças entre as várias implementações de interceptor.
3. Dependências do Maven
Para usarInterceptors, você precisa incluir a seguinte seção em uma seçãodependencies de seu arquivopom.xml:
org.springframework
spring-web
5.0.6.RELEASE
A última versão pode ser encontradahere.
4. Interceptador de manipulador de mola
Interceptadores trabalhando comHandlerMapping na estrutura devem implementar a interfaceHandlerInterceptor.
Essa interface contém três métodos principais:
-
prehandle() - chamado antes que o manipulador real seja executado, mas a visualização ainda não foi gerada
-
postHandle() - chamadoafter o manipulador é executado
-
afterCompletion() – chamadoafter the complete request has finished and view was generated
Esses três métodos fornecem flexibilidade para executar todos os tipos de pré e pós-processamento.
E uma nota rápida - a principal diferença entreHandlerInterceptoreHandlerInterceptorAdapter é que no primeiro precisamos substituir todos os três métodos:preHandle(),postHandle()eafterCompletion(), enquanto no segundo podemos implementar apenas os métodos necessários.
Uma nota rápida antes de prosseguirmos - se você quiser pular a teoria e pular direto para os exemplos, pule direto para a seção 5.
Esta é a aparência de uma implementação simples depreHandle():
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// your code
return true;
}
Observe que o método retorna um valorboolean - que diz ao Spring se a solicitação deve ser processada posteriormente por um manipulador (true) ou não (false).
A seguir, temos uma implementação depostHandle():
@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
// your code
}
Este método é chamado imediatamente após a solicitação ser processada porHandlerAdapter, mas antes de gerar uma visualização.
E, é claro, pode ser usado de várias maneiras - por exemplo, podemos adicionar um avatar de um usuário conectado a um modelo.
O método final que precisamos implementar na implementação personalizadaHandlerInterceptor éafterCompletion():
@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
// your code
}
Quando a visualização é gerada com sucesso, podemos usar esse gancho para fazer coisas como reunir estatísticas adicionais relacionadas à solicitação.
Uma nota final a lembrar é que umHandlerInterceptor é registrado no beanDefaultAnnotationHandlerMapping, que é responsável por aplicar interceptores a qualquer classe marcada com uma anotação@Controller. Além disso, você pode especificar qualquer número de interceptores em seu aplicativo da web.
5. Interceptor Logger Customizado
Neste exemplo, focaremos no login em nosso aplicativo da web. Em primeiro lugar, nossa classe precisa estenderHandlerInterceptorAdapter:
public class LoggerInterceptor extends HandlerInterceptorAdapter {
...
}
Também precisamos habilitar o logon em nosso interceptador:
private static Logger log = LoggerFactory.getLogger(LoggerInterceptor.class);
Isso permite que o Log4J exiba logs e indique qual classe está registrando as informações atualmente na saída especificada.
A seguir, vamos nos concentrar nas implementações de interceptores personalizados:
5.1. MétodopreHandle()
Este método é chamado antes de manipular uma solicitação; ele retornatrue, para permitir que a estrutura envie a solicitação para o método manipulador (ou para o próximo interceptor). Se o método retornarfalse, o Spring assume que a solicitação foi tratada e nenhum processamento adicional é necessário.
Podemos usar o gancho para registrar informações sobre os parâmetros dos pedidos: de onde vem o pedido, etc.
Em nosso exemplo, estamos registrando essas informações usando um simples log4J:
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
log.info("[preHandle][" + request + "]" + "[" + request.getMethod()
+ "]" + request.getRequestURI() + getParameters(request));
return true;
}
Como podemos ver, estamos registrando algumas informações básicas sobre a solicitação.
No caso de encontrarmos uma senha aqui, precisamos nos certificar de que não a registramos, é claro.
Uma opção simples é substituir senhas e qualquer outro tipo de dados confidencial por estrelas.
Esta é uma implementação rápida de como isso pode ser feito:
private String getParameters(HttpServletRequest request) {
StringBuffer posted = new StringBuffer();
Enumeration> e = request.getParameterNames();
if (e != null) {
posted.append("?");
}
while (e.hasMoreElements()) {
if (posted.length() > 1) {
posted.append("&");
}
String curr = (String) e.nextElement();
posted.append(curr + "=");
if (curr.contains("password")
|| curr.contains("pass")
|| curr.contains("pwd")) {
posted.append("*****");
} else {
posted.append(request.getParameter(curr));
}
}
String ip = request.getHeader("X-FORWARDED-FOR");
String ipAddr = (ip == null) ? getRemoteAddr(request) : ip;
if (ipAddr!=null && !ipAddr.equals("")) {
posted.append("&_psip=" + ipAddr);
}
return posted.toString();
}
Por fim, nosso objetivo é obter o endereço IP de origem da solicitação HTTP.
Aqui está uma implementação simples:
private String getRemoteAddr(HttpServletRequest request) {
String ipFromHeader = request.getHeader("X-FORWARDED-FOR");
if (ipFromHeader != null && ipFromHeader.length() > 0) {
log.debug("ip from proxy - X-FORWARDED-FOR : " + ipFromHeader);
return ipFromHeader;
}
return request.getRemoteAddr();
}
5.2. MétodopostHandle()
Este gancho é executado quandoHandlerAdapter é invocado pelo manipulador, masDispatcherServlet ainda não renderizou a visualização.
Podemos usar este método para adicionar atributos adicionais aoModelAndView ou para determinar o tempo gasto pelo método do manipulador para processar a solicitação de um cliente.
Em nosso caso, simplesmente registramos uma solicitação antes deDispatcherServlet renderizar uma visualização.
@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
log.info("[postHandle][" + request + "]");
}
5.3. MétodoafterCompletion()
Quando uma solicitação é concluída e a exibição é renderizada, podemos obter dados de solicitação e resposta, além de informações sobre exceções, se houver alguma:
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex)
throws Exception {
if (ex != null){
ex.printStackTrace();
}
log.info("[afterCompletion][" + request + "][exception: " + ex + "]");
}
6. Configuração
Para adicionar nossos interceptores na configuração do Spring, precisamos substituir o métodoaddInterceptors() dentro da classeWebConfig que implementaWebMvcConfigurer:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggerInterceptor());
}
Podemos obter a mesma configuração editando nosso arquivo de configuração XML Spring:
Com essa configuração ativa, o interceptador estará ativo e todas as solicitações no aplicativo serão registradas corretamente.
Observe, se vários interceptores Spring estiverem configurados, o métodopreHandle() é executado na ordem da configuração, enquanto os métodospostHandle()eafterCompletion() são chamados na ordem inversa.
7. Conclusão
Este tutorial é uma introdução rápida à interceptação de solicitações HTTP usando o Spring MVC Handler Interceptor.
Todos os exemplos e configurações estão disponíveis aqui emGitHub.