irundaia irundaia - 3 months ago 43
Java Question

Spring boot HandlerInterceptor loadbalancing

I'm implementing a (sort of) load balancing

HandlerInterceptor
using Spring Boot.

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
if (shouldUseServer1(uri)) {
response.sendRedirect(server1Uri);
} else {
response.sendRedirect(server2Uri);
}
}


The idea is, that based on the url, we either redirect to one service or another. The application doesn't have any explicit
RequestMapping
s (yet).

Now the problem is, when the interceptor is called, the request is redirected to the default Spring error handler. As a result the URI stored in the
HttpServletRequest
is replaced by
/error
(effectively denying the access to the original URI).

Is there any way to intercept a request before it is rerouted to the error handler (or to get the original uri)?

Answer

EDIT:

Because of the way Spring MVC handles requests with no mapping, you'll either need a filter:

@Component
public class CustomFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        request.getSession().setAttribute("ORIGINAL_REQUEST_URI", request.getRequestURI());
        chain.doFilter(request, response);

        // alternatively, ignore the last 2 lines
        // and just do your redirects from here 
        // and don't continue the filter chain
    }

  @Override
  public void destroy() {}

  @Override
  public void init(FilterConfig arg0) throws ServletException {}

}

Otherwise, if you'd rather not rely on the session, you'll need to make the DispatcherServlet throw an exception in case no handler mapping is found, and then send the redirect from a @ControllerAdvice error handler:

@ControllerAdvice
class NoHandlerFoundExceptionExceptionHandler {

  @ExceptionHandler(value = NoHandlerFoundException.class)
  public ModelAndView
  defaultErrorHandler(HttpServletRequest req, NoHandlerFoundException e) throws Exception {
    String uri = // resolve the URI
    return new ModelAndView("redirect:" + uri);
  }
}

To avoid duplication, you may want to have a common class that you'll call from both the interceptor and the error handler.