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

3

9

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?

Urease answered 20/9, 2016 at 8:25 Comment(0)
I
6

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. :)

Impurity answered 25/5, 2021 at 12:7 Comment(0)
T
5

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());
}
Tsui answered 18/10, 2018 at 19:58 Comment(0)
P
1

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.

Peale answered 20/9, 2016 at 8:39 Comment(4)
Isn't there a better way than this?Urease
This is how Spring Exception Handler works. If you are using spring you can go with this.Peale
Please comment the reason when you downvote an answer.Peale
@Peale this is the vanilla way to do the exception handling. In this case, the status code is set "statically" not "dinamically" based on information within the exception class.Tsui

© 2022 - 2024 — McMap. All rights reserved.