3

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 RequestMappings (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)?

irundaia
  • 1,720
  • 17
  • 25

1 Answers1

1

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.

Community
  • 1
  • 1
Miloš Milivojević
  • 5,219
  • 3
  • 26
  • 39
  • The redirect works fine. The problem is that I need the original URI from the request. In case there is no `RequestMapping` for the original URI, Spring will automatically redirect(?) to the default error controller. This redirection also changes the URI in the `HttpServletRequest` to `/error` (or some such). As a result, I can no longer access the original URI (to determine where I need to redirect to). My current workaround is to have a simple controller with a `RequestMapping(path = "**")`, but that seems like a dirty hack. – irundaia Sep 05 '16 at 08:48
  • Ah, sorry, from your description I understood that the redirection to `/error` is happening in spite of the interceptor's redirect. I'll update my answer then, I think you should be okay with having a `Filter` that just saves the original request URI as a request attribute. – Miloš Milivojević Sep 05 '16 at 09:03
  • There, hope that helps. Your way of using a controller definitely requires less work and if I may be frank, probably is less of a hack :) Also, if you're just doing redirection, you're probably better off with a `Filter` implementation instead of using an interceptor/controller approach. – Miloš Milivojević Sep 05 '16 at 09:36
  • It did help. Thanks a bunch for the effort :) – irundaia Sep 05 '16 at 09:38
  • Don't mention it, I learned a few things along the way, too :) – Miloš Milivojević Sep 05 '16 at 09:41