0

Is there a way in which we can generate the JSON response from within the code that an API endpoint would show when a controller is called.

I basically want the application to call one of its own controllers and generate the response as a JSON string but not by making an HTTP call.

Calling the controller method and applying an objectmapper on it gives back the results just fine, but when there are exceptions it throws it through. However, I want it to generate the error response that would have generated from @ControllerAdvice exception handlers.

Is it possible to apply the exception handlers defined in @ControllerAdvice to an exception caught in a variable from within the application (Not by making an HTTP call)?

I am building a bulk api so that instead of the user calling an api multiple times, they can just put a list of payloads to this new bulk api, and internally I want to save the responses of each subrequest within the bulk api's db entity itself, and for that I need the exact json responses for each subrequest as if the original api was called.

I can do an exception handling separately for this bulk api code but then that would duplicate the code I already have in my controller advice.

One solution I have come up with is given below. However, if I use it I'll have to remove all my current exception handlers and just use one single exception handler accepting Exception.

Controller Advice:

    @ExceptionHandler(ConversionFailedException.class)
    public ResponseEntity<ErrorResponse> handleConversionFailedException(final ConversionFailedException e) {

        return buildResponseEntity(HttpStatus.BAD_REQUEST, e);
    }

    @ExceptionHandler(value = {ActionNotFoundException.class})
    public ResponseEntity<ErrorResponse> handleActionNotFoundException(ActionNotFoundException e) {

        return buildResponseEntity(HttpStatus.NOT_FOUND, e);
    }


    // someone just added it
    @ExceptionHandler(value = {NewRuntimeException.class})
    public ResponseEntity<ErrorResponse> handleNewRuntimeException(NewRuntimeException e) {

        return buildResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR, e);
    }

Bulk API code:

    try {


    }catch(ConversionFailedException e){
        return buildResponseEntity(HttpStatus.BAD_REQUEST, e);
    }catch(ActionNotFoundException e){
        return buildResponseEntity(HttpStatus.NOT_FOUND, e);
    }
    // they forgot to add it here

My current solution:

Controller Advice:

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleAllExceptions(final Exception e) {

        return ExceptionHandlerUtil.funcA(e);
    }

ExceptionHandlerUtil class (New):

    private ResponseEntity<ErrorResponse> funcA(Exception e) {

        if (e instanceof ConversionFailedException) {
            return buildResponseEntity(HttpStatus.BAD_REQUEST, e);
        }
        else if (e instanceof ActionNotFoundException) {
            return buildResponseEntity(HttpStatus.NOT_FOUND, e);
        }
        // they can add any new exception here, and both the parts of the code will be able to handle it
    }

Bulk API code:

    try {


    }catch(Exception e){
        return ExceptionHandlerUtil.funcA(e);
    }
Saif
  • 2,530
  • 3
  • 27
  • 45
  • No. The `@ControllerAdvice` is, as the name implies, for `@(Rest)Controllers` not for aribitrary other components. You could use regular AOP, but to what end? – M. Deinum Jun 28 '22 at 05:47
  • Yeah I totally agree, but is there a way in which I can take a caught exception through all the current exception handles in my @ControllerAdvice? Maybe by adding another annotation to all of them or something. The thing is if I don't go this way, then I'll have to write a switch case doing the case thing as the current exception handlers, ultimately duplicating the code. – Saif Jun 28 '22 at 05:55
  • As stated you can use regular AOP but why would you need your regular exception handling to convert stuff into web related exception handling? I also don't get why you suddently need to use a switch expression in this case. – M. Deinum Jun 28 '22 at 05:59
  • Right, so this may sound like an anti-pattern. But I am building a bulk api so that instead of the user calling an api multiple times, they can just put a list of payloads to this new bulk api, and internally I want to save the responses of each subrequest within the bulk api's db entity itself, and for that I need the exact json responses for each subrequest as if the original api was called. – Saif Jun 28 '22 at 08:34
  • Which seems overly complex! You already have a method that receives the bulk request, write an error handler that handles the errors and doesn't really care about 1 or more parts of the error. – M. Deinum Jun 28 '22 at 09:02
  • Yes, that I can do. But I'll be the same code as the controller-advice. Also, it puts the bulk api at risk of managing an exception differently if someone adds a new exception handler in controller advice and forgets to do the same in the bulk api code. – Saif Jun 28 '22 at 09:15
  • You still have that problem even with the added (if it existed) functionality. Unless you route the bulk API to individual requests to the original endpoint, collect all individual responses and merge them you cannot do this without additional code (althouhg that workaround is additional code as well). – M. Deinum Jun 28 '22 at 09:17
  • The additional code required in calling the service methods multiple times + saving the responses as json does not duplicate the code. I can call the same method that the controller calls and use an object mapper on it. Also, if something changes in the individual service method, both the original controller and the bulk api would be calling the same changed code. However, that's not the case with exceptions. Both will be handled at two different places. – Saif Jun 28 '22 at 09:21
  • If you write your exception handling correcty you don't need duplicate code. And no you cannot call the same method as that bypasses exception handling, without going through the servlet again (making it a new request) you cannot re-use the existing code. But as stated if you design your exceptionhandling properly you wouldn't need different methods or duplicate code. – M. Deinum Jun 28 '22 at 09:22
  • One solution I have is to have just one @ExceptionHandler method that takes in an Exception (java.lang.Exception) and passes it to another method (let's say funcA), and funcA uses a chain of instanceOfs to return the right ReponseEntity for each type of exception. The same funcA can be called from the bulk api code as well when it catches an Exception. But the problem with this is that I'll end up with just one ExceptionHandler taking in java.lang.Exception, whereas in my current codebase I have multiple ExceptionHandlers for each type of Exception that we usually throw. – Saif Jun 28 '22 at 09:28
  • Why would the type of `Exception` need to change. You should provide the needed information to convert to a more meaningful response into the `Exception`. Include a collection/array of objects that failed (maybe with messages), and proces that in your handler. 1 method for a single or multiple error response... – M. Deinum Jun 28 '22 at 09:38
  • Added some code for clarification. – Saif Jun 28 '22 at 15:19

0 Answers0