Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return HTTP status code dynamically as per the request using @ExceptionHandler

I want to return HTTPStatus code dynamically like 400, 400, 404 etc as per the response object error. I was referred to this question - Programmatically change http response status using spring 3 restful but it did not help.

I have this Controller class with an @ExceptionHandler method

@ExceptionHandler(CustomException.class)
    @ResponseBody
    public ResponseEntity<?> handleException(CustomException e) {
        return new ResponseEntity<MyErrorResponse>(
                new MyErrorResponse(e.getCode(), ExceptionUtility.getMessage(e.getMessage())), 
                ExceptionUtility.getHttpCode(e.getCode()));
    }

ExceptionUtility is a class where I have the two methods used above (getMessage and getCode).

public class ExceptionUtility {
    public static String getMessage(String message) {
        return message;
    }

    public static HttpStatus getHttpCode(String code) {
        return HttpStatus.NOT_FOUND; //how to return status code dynamically here ?
    }
}

I do not want to check in if condition and return the response code accordingly, Is there any other better approach to do this?

like image 584
user2340345 Avatar asked Sep 20 '16 08:09

user2340345


Video Answer


3 Answers

First Approach:

You can take the HTTPStatus field in your customException class. for e.g.

    class CustomException {
         HttpStatus httpStatus;
         ....other fields
    }

You can throw an error like:

throw new CustomException(otherField , HttpStatus.CREATED);

And in your exception handler you can do something like:

@ExceptionHandler(CustomException.class)
    @ResponseBody
    public ResponseEntity<?> handleException(CustomException e) {
        return new ResponseEntity<MyErrorResponse>(
                new MyErrorResponse(e.getCode(), ExceptionUtility.getMessage(e.getMessage())), 
                e.getHttpStatus());
    }

where, e.getHttpStatus() returns HTTPStatus.

Second Approach:

Or you can declare enum for your code like this:

public enum ErrorCode {


    MyErrorCode(HttpStatus.OK);

    HttpStatus status;

//getter for httpstatus
}

and then you can modify your exception handler to

@ExceptionHandler(CustomException.class)
        @ResponseBody
        public ResponseEntity<?> handleException(CustomException e) {
            return new ResponseEntity<MyErrorResponse>(
                    new MyErrorResponse(e.getCode(), ExceptionUtility.getMessage(e.getMessage())), 
                    e.getHttpStatus());
        }

where again e.getHttpStatus() is method call on enum, but for this you have to change your code field type in your custom exception and if you don't want to do that also, then you can write one helper method in enum something like:

HttpStatus getHttpStatus(String code) {
   return ErrorCode.valueOf(code.toUpperCase()).getHttpStatus();
}

sorry I missed the null pointer exception in the above method but you can modify that according to your need, just giving an idea. :)

like image 152
aditya lath Avatar answered Oct 18 '22 03:10

aditya lath


Your @ExceptionHandler method needs two things: (i) have an HttpServletResponse parameter, so that you can set the response status code; and (ii) do not have the @ResponseStatus annotation.

@ExceptionHandler(CustomException.class)
@ResponseBody
public ResponseEntity<?> handleException(HttpServletResponse resp, CustomException e) {
    resp.setStatus(ExceptionUtility.getHttpCode(e.getCode()).value());
    return new ResponseEntity<MyErrorResponse>(
            new MyErrorResponse(e.getCode(), ExceptionUtility.getMessage(e.getMessage())),
            ExceptionUtility.getHttpCode(e.getCode()));
}

I have used this kind of @ExceptionHandler to handle NestedServletException, which is an exception that Spring sometimes creates to wrap the "actual" exception that needs to be treated (code below). Note that the @ExceptionHandler method can have both request and response objects as parameters, if you need them.

@ExceptionHandler(NestedServletException.class)
@ResponseBody
public Object handleNestedServletException(HttpServletRequest req, HttpServletResponse resp, 
        NestedServletException ex) {
    Throwable cause = ex.getCause();
    if (cause instanceof MyBusinessLogicException) {
        resp.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value());
        return createStructureForMyBusinessLogicException((MyBusinessLogicException) cause);
    }
    if (cause instanceof AuthenticationException) {
        resp.setStatus(HttpStatus.UNAUTHORIZED.value());
    } else if (cause instanceof AccessDeniedException) {
        resp.setStatus(HttpStatus.FORBIDDEN.value());
    } else {
        resp.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    }
    return createStructureForOtherErrors(req, cause.getMessage(), resp.getStatus());
}
like image 28
Paulo Merson Avatar answered Oct 18 '22 04:10

Paulo Merson


You need to define different Exception handler for different Exception and then use @ResponseStatus as below:

@ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler({ UnAuthorizedException.class })
    public @ResponseBody ExceptionResponse unAuthorizedRequestException(final Exception exception) {

        return response;
    }

@ResponseStatus(HttpStatus.CONFLICT)
    @ExceptionHandler({ DuplicateDataException.class })
    public @ResponseBody ExceptionResponse DuplicateDataRequestException(final Exception exception) {

        return response;
    }

@ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({ InvalidException.class })
    public @ResponseBody ExceptionResponse handleInvalidException(final Exception exception) {

        return response;
    }

Here the InvalidException.class, DuplicateDataException.class etc are examples. You can define your custom Exceptions and throw them from controller layer. for example you can define a UserAlreadyExistsException and return the HttpStatus.CONFLICT error code from your exception handler.

like image 1
Suraj Avatar answered Oct 18 '22 02:10

Suraj