Home > OS >  Force function to throw and return specific Exception to client results in throwing "Invocation
Force function to throw and return specific Exception to client results in throwing "Invocation

Time:08-24

When the user creates a record with a name that's already exists in the DB , I'm returning a specific Exception.

  @PostMapping("/campaigns")
    public ResponseEntity<CampaignDTO> saveCampaign(@RequestBody CampaignDTO campaignDTO) throws ApiErrorResponse, Exception {
        if (this.campaignService.getCampaignByName(campaignDTO.getName()) != null) {
            throw new IllegalArgumentException("The value already exists!");
        }
        if (campaignDTO.getProducts() == null) {
            ApiErrorResponse errorReponseDto = new ApiErrorResponse("No Products attached");
            throw errorReponseDto;
        }
        campaignDTO = campaignService.saveCampaign(campaignDTO);
        ResponseEntity<CampaignDTO> responseEntity = new ResponseEntity<>(campaignDTO , HttpStatus.CREATED);
        return responseEntity; // return 201
    }

And the Exception that I want to return to the Client is:

public class ApiErrorResponse extends Throwable {
    private final String error;
    //Any addtional info you might later want to add to it
    public ApiErrorResponse(String error){
        this.error = error;
    }

    public String getError(){
        return this.error;
    }
}

However , when I throw

IllegalArgumentException("The value already exists!")

It is caught by

catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            }
            else if (targetException instanceof Error) {
                throw (Error) targetException;
            }
            else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            }
            else {
                throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
            }
        }

How can we prevent this , and return "ApiErrorResponse" when the user inserts the same name ?

I want to return my Exception , not anything else.

CodePudding user response:

Here you have few decision. I will mark 2 of them. 1st is to return directly BadRequest for example with specific DTO - It is not best example with the Throwable, but you can create new ErrorResponseDTO:

@PostMapping("/campaigns")
public ResponseEntity<?> saveCampaign(@RequestBody CampaignDTO campaignDTO) throws ApiErrorResponse, Exception {
    if (this.campaignService.getCampaignByName(campaignDTO.getName()) != null) {
        ApiErrorResponse errorReponseDto = new ApiErrorResponse("The value already exists!");
        return ResponseEntity.badRequest().body(errorReponseDto) // return 400
    }
    if (campaignDTO.getProducts() == null) {
        ApiErrorResponse errorReponseDto = new ApiErrorResponse("No Products attached");
        return new ResponseEntity<>(errorReponseDto , HttpStatus.BAD_REQUEST); // return 400
    }
    campaignDTO = campaignService.saveCampaign(campaignDTO);
    ResponseEntity<CampaignDTO> responseEntity = new ResponseEntity<>(campaignDTO , HttpStatus.CREATED);
    return responseEntity; // return 201
}

Other way is to use @ControllerAdvice which will handle exception and will return what you want. This advice will be triggered after you throw the exception:

@ControllerAdvice
public class MyAdvice {
@ExceptionHandler(value = ApiErrorResponse.class)
public ResponseEntity<MyErrorResponse> handleException(ApiErrorResponse exception) {
    return return ResponseEntity.badRequest().body(MyErrorResponse)
}

}

CodePudding user response:

a better way to define a global exception class and a global exception handler.

  • the global exception class:
@EqualsAndHashCode(callSuper = true)
@Data
public class GlobalErrorInfoException extends RuntimeException {
    private String message;
    private HttpStatus status;
    private Long timestamp;

    public GlobalErrorInfoException(HttpStatus status, String message) {
        super(message);
        this.status = status;
        this.message = message;
        this.timestamp = System.currentTimeMillis();
    }
}
  • the global exception handler
@RestControllerAdvice
@Slf4j
public class GlobalErrorInfoHandler {
    // other ExceptionHandler

    // GlobalErrorInfoException handler
    @ExceptionHandler(value = GlobalErrorInfoException.class)
    public ResponseEntity<?> errorHandlerOverJson(GlobalErrorInfoException e) {
        log.error("Global Exception ", e);
        return new ResponseEntity<>(e.getMessage(), e.getStatus());
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> handleRuntimeException(Exception e) {
        log.error("error ", e);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("internal server error");
    }
}
  • use
// your throw
throw new GlobalErrorInfoException(HttpStatus.BAD_REQUEST, "The value already exists!");

you can also handler IllegalArgumentException in the global exception handler.

  • Related