@ControllerAdvice: Get the correct HttpStatus when an exception is thrown

Dear sirs,
I am setting up a global exception handler in Spring. Once an exception is caught, a method in the @ControllerAdvice is called in, and returns a specific view with exception details. Among info returned with the view, I have the HttpStatus, that can get. Actually I keep getting 200, while the right HttpStatus should be 500, 404, etc.
Here is my code
@ControllerAdvice
@Slf4j
public class AppGlobalExceptionHandler {
      /*
       * Note: You can either point to Exception Types or Http ResponseStatus
       * @ExceptionHandler({MyException.class})
       * public String ...
       *
       * @ExceptionHandler
       * @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
       * public String
       * */
      
      @ExceptionHandler
      public String handleAnyException(Exception exception, Model model,
                  HttpServletRequest request, HttpServletResponse response) {
            
            log.error("Request raised " + exception.getClass().getSimpleName());
            
            String details = ExceptionUtils.getStackTrace(exception);
            
            AppError error = new AppError();
            error.setStatus(response.getStatus());
            error.setUrl(String.valueOf(request.getRequestURL()));
            error.setMessage(exception.getMessage());
            error.setDetails(details);
            
            model.addAttribute(AppStringHandler.VARIABLE_URL_SUBMIT, AppStringHandler.URL_TASKS_SEND_EMAIL_500);
            model.addAttribute(AppStringHandler.VARIABLE_URL_REDIRECT, AppStringHandler.URL_ADMIN_REDIRECT);
            model.addAttribute(AppStringHandler.VIEW_ERROR_MODEL_ATTRIBUTE, error);
            
            return AppStringHandler.VIEW_ADMIN_ERROR_ANY;
      }

Please, assist
Omer-PitouAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

girionisCommented:
The @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)  should be outside the javadoc comments.
0
Omer-PitouAuthor Commented:
Hi Girionis,
I tried that before without success.
It was still returning 200 instead of the right HttpStatus from the HttpServletResponse.

Thanks
0
girionisCommented:
You must be doing something wrong then because I just tried it and it works fine.

Things to notice: Do not pass the model and exception to the method, instead create the model in the method if you need.

The method signature should look like the following:

public String handleAnyException(HttpServletRequest request, HttpServletResponse response) {

Open in new window


For more information have a look at Spring's exception handling tutorial.
0
Cloud Class® Course: Microsoft Windows 7 Basic

This introductory course to Windows 7 environment will teach you about working with the Windows operating system. You will learn about basic functions including start menu; the desktop; managing files, folders, and libraries.

Omer-PitouAuthor Commented:
Hi Girionis,
I fully agree with you not to pass the Model in the method in this case. I still need the exception passed, for me to get details to display on the view returned by this ExceptionHandler.
My main problem is to catch the HttpStatus returned when the Exception occurred (is it a 404, 500, etc...). That's where I am struggling. The new code looks like this:
@ExceptionHandler
@ResponseStatus
      public ModelAndView handleAnyException(Exception exception,
                  HttpServletRequest request, HttpServletResponse response) {
            
            log.error("Request raised " + exception.getClass().getSimpleName());
            
            String details = ExceptionUtils.getStackTrace(exception);
            
            AppError error = new AppError();
            error.setStatus(response.getStatus()); // ??? I want the right status here, I mean the httpStatus return when the Exception leads to this ControllerAdvice method
            error.setUrl(String.valueOf(request.getRequestURL()));
            error.setMessage(exception.getMessage());
            error.setDetails(details);
            error.setException(exception); //To manage in FORMAT Email
            
            Map<String, Object> map = new HashMap<String, Object>();
            map.put(AppStringHandler.VARIABLE_URL_SUBMIT, AppStringHandler.URL_TASKS_SEND_EMAIL_500);
            map.put(AppStringHandler.VARIABLE_URL_REDIRECT, AppStringHandler.URL_HOME);
            map.put(AppStringHandler.VIEW_ERROR_MODEL_ATTRIBUTE, error);
            
            ModelAndView model = new ModelAndView();
            model.addAllObjects(map);
            model.setViewName(AppStringHandler.VIEW_ADMIN_ERROR_ANY);
            
            return model;
      }
0
girionisCommented:
error.setStatus(response.getStatus()); // ??? I want the right status here, I mean the httpStatus return when the Exception leads to this ControllerAdvice method

The error.setStatus(response.getStatus()); should work.
0
Omer-PitouAuthor Commented:
"The error.setStatus(response.getStatus()); should work."
It gives me a 200 status, not the original Status linked to the Exception that lead to that ControllerAdvice method.
0
mccarlIT Business Systems Analyst / Software DeveloperCommented:
Actually, I don't think you can do what you are wanting to do anyway, because of the order that spring does things. The status codes that you are after (404, 500, etc) are all set by Spring's default exception handlers, but they don't get executed when you supply your own ExceptionHandler (or at least they don't get executed until AFTER your handlers run, in the case where your handler may throw or rethrow an exception). That's why you are getting status of 200 all the time.

I don't think there is any way to hook into the processing AFTER the default handlers have run, so I think your only option is to work out the status code yourself based on the exception thrown. This shouldn't be a major problem though, you should have a pretty good idea of what specific exceptions your own code might throw and set status appropriately, and then for any exception that you catch that you don't know about, just set the status to 500. (That is pretty much what the default Spring handler would be doing anyway)
0
girionisCommented:
It gives me a 200 status, not the original Status linked to the Exception that lead to that ControllerAdvice method.

This is normal. By the time you intercept the exception in your @ExceptionHandler, Spring hasn't yet set the correct response status. The response status will be set in two ways:

1) You set it yourself in the @ExceptionHandler
2) Spring will set it when the default error handler will run.

If you want to achieve what you're asking you will need to manually pass the status in your @RequestMapping endpoint.

@RequestMapping(value = "/test", method = RequestMethod.POST)
public String test(HttpServletRequest request, HttpServletResponse response) throws FileNotFoundException {
		response.setStatus(404);
		throw new FileNotFoundException("NOT FOUND.");
	}

Open in new window


By doing this when the flow reaches your @ControllerAdvice then the

response.getStatus()

Open in new window


should return 404.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Java

From novice to tech pro — start learning today.