spring mvc @ExceptionHandler method get same view
Asked Answered
S

5

10

My problem is that I want to create an @ExceptionHandler method that will capture all un-handled exceptions. Once captured I would like to redirect to the current page instead of specifying a separate page just to display error.

Basically how do I get the value of someview returned by somemethod and set it dynamically in the method unhandledExceptionHandler below.

@ExceptionHandler(Exception.class)
protected ModelAndView unhandledExceptionHandler(Exception ex){
    System.out.println("unhandle exception here!!!");
    ModelAndView mv = new ModelAndView();
    mv.setViewName("currentview");
    mv.addObject("UNHANDLED_ERROR", "UNHANDLED ERROR. PLEASE CONTACT SUPPORT. "+ex.getMessage());
    return mv;
}



@RequestMapping(value = "/somepage", method = RequestMethod.GET)
public String somemethod(HttpSession session) throws Exception {
    String abc = null;
    abc.length();
    return "someview";
}

So in JSP I can render this error message back into the current page something like that.

<c:if test="${not empty UNHANDLED_ERROR}">
    <div class="messageError"> ${UNHANDLED_ERROR}</div>
</c:if>
Spaghetti answered 16/9, 2013 at 16:11 Comment(3)
@aaron-blenkush I have done work on this but in a one-controller-class-per-mapped-request, not with methods, so not sure if interestingVergne
Bounty expires soon! This can be resolved with 1-2 lines of code. Cheers.External
@AaronBlenkush, so you have an answer? Is there any chance you can share it? Really want to look at it :)Eupatorium
J
6

I don't think there is way to do what you are asking for, because in the exception handler method unhandledExceptionHandler there is no way to find out what the name of the view that the handler method somemethod would have returned.

The only way is for you to introduce some sort of meta data scheme so that when you end up in the exception handler you can figure out what view to map it to. But I think this meta data scheme would be fairly complex. You can implement such a scheme by finding out what was the original url being accessed when the exception was thrown, this can be done with the code snippet below.

(ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()

Once you know what the original request URL you can redirect to it, maybe using flash attribute to store the fact that there was an exception and what the error is.

The main problem wit the metadata will occur when you have a handler method that select between different views something like.

@RequestMapping(value = "/somepage", method = RequestMethod.GET)
public String somemethod(HttpSession session) throws Exception {
    String abc = null;
    if(someCondition) {
        abc.length();
        return "someview";
    } else {
        // do some stuff here.
        return "someOtherView";
    }
}

Even knowing that somemethod was the source of the error leaves you not knowing which branch in the if statement caused the exception.

Jessiajessica answered 16/9, 2013 at 16:49 Comment(1)
Thanks. I guess I'll just have to redirect to a error page for now. I'll just leave this question open for few days. if still no response I'll just accept it as an answer..Spaghetti
E
6

I dont think you can do this without modifying all of your handler methods. However you can try to do this in a "pretty" way:

1) You can define your own annotation which will accept target view name as a parameter (e.g. @ExceptionView)

2) Next thing to do is marking your handler methods with it, e.g.:

@ExceptionView("someview")
@RequestMapping(value = "/somepage", method = RequestMethod.GET)
    public String somemethod(HttpSession session) throws Exception {
    String abc = null;
    abc.length();
    return "someview";
}

3) After that you can do something like this in exception handler:

@ExceptionHandler(Exception.class)
protected ModelAndView unhandledExceptionHandler(Exception ex, HandlerMethod hm) {
    String targetView;
    if (hm != null && hm.hasMethodAnnotation(ExceptionView.class)) {
        targetView = hm.getMethodAnnotation(ExceptionView.class).getValue();
    } else {
        targetView = "someRedirectView"; // kind of a fallback
    }
    ModelAndView mv = new ModelAndView();
    mv.setViewName(targetView);
    mv.addObject("UNHANDLED_ERROR", "UNHANDLED ERROR. PLEASE CONTACT SUPPORT. "+ex.getMessage());
    return mv;
}
Eupatorium answered 28/6, 2017 at 11:58 Comment(0)
H
2

Rather than sending the error on a separate page, you can you just put the error in the ModelAndView object. In your case you can just put the try/catch in your controller method and return the same view like so:

@RequestMapping(value = "/somepage", method = RequestMethod.GET)
public String somemethod(ModelAndView mv,HttpSession session) throws Exception {
   mv.setViewName("someview");
   try{ 
      String abc = null;
      abc.length();
    } catch(Exception e) {
      mv.addObject("UNHANDLED_ERROR", "UNHANDLED ERROR. PLEASE CONTACT SUPPORT.  "+ex.getMessage());
   }
   return mv;
}

So add the ModelAndView to your method and return it.

Haemato answered 21/9, 2013 at 11:30 Comment(1)
This way I would need to to do this on every method which I'm trying to avoid.Spaghetti
C
2

I have not tried this out, but based on the documentation here, we can get the request object in the exception handler. We may not be able to get the view linked to the URL. Getting the view from the URL, and the state/model of the view will be the tricky part.

  @ExceptionHandler(Exception.class)
  public ModelAndView handleError(HttpServletRequest req, Exception ex) {
    logger.error("Request: " + req.getRequestURL() + " raised " + ex);

    ModelAndView mav = new ModelAndView();
    mav.addObject("exception", ex);
    mav.addObject("url", req.getRequestURL());
    mav.setViewName("error");
    return mav;
  }
Cytogenesis answered 30/6, 2017 at 20:11 Comment(0)
E
0
  1. Create a controller method annotated with @RequestMethod("/server-error")
  2. Create a controller method annotated with @ExceptionHandler which will return "forward:/server-error";
External answered 5/7, 2017 at 13:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.