Return HTTP status code dynamically as per the req

2019-05-06 21:21发布

问题:

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?

回答1:

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());
}


回答2:

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.