0

I have a spring boot application currently in production. It has APIs which return the error in this format

{
   "success": true,
   "error": "Error message" 
}

This error response I'm handling through the controller advisor.

I'm creating v2 APIs and wanted to change the error response structure for the newer APIs. I want to write code in such a way, that It can have two rest advisors one is handling errors for the older APIs, and the other is for v2 APIs. We can have multiple rest advisors in Spring boot and we can set the order of execution using @Order annotation but for my use case, I want to get it executed based on the API path, If it has v2 it should be executed first otherwise it should get skipped.

@RestControllerAdvice(annotations = RestController.class)
@Log4j2
public class V1APIExceptionHandler extends ResponseEntityExceptionHandler {
    
    @ExceptionHandler(NotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public V1ErrorResponse handleNotFoundException(NotFoundException ex) {
            return V1ErrorResponse.builder().error(ex.getMessage()).build();
        }
    }


@RestControllerAdvice(annotations = RestController.class)
@Log4j2
public class V2APIExceptionHandler extends ResponseEntityExceptionHandler {
    
    @ExceptionHandler(NotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public V2ErrorResponse handleNotFoundException(NotFoundException ex) {
            return V2ErrorResponse.builder().error(ex.getMessage()).build();
        }
    }


@GetMapping(value = "v1/user/{id}")
    public UserDetails getUser(
            @PathVariable("id") String userId
    ) throws NotFoundException {
        return userService.getUserDetails(userId);
    }

@GetMapping(value = "v2/user/{id}")
    public UserDetails getUser(
            @PathVariable("id") String userId
    ) throws NotFoundException {
        return userService.getUserDetails(userId);
    }

1 Answers1

0

We can keep v1 and v2 API in different controllers in different packages. Then we should be able to use basePackages param of @RestControllerAdvice

basepackage.controller
   - v1
      - ControllerV1.java
   

     @GetMapping(value = "v1/user/{id}")
        public UserDetails getUser(
                @PathVariable("id") String userId
        ) throws NotFoundException {
            return userService.getUserDetails(userId);
        }

   - v2
      - ControllerV2.java

    @GetMapping(value = "v2/user/{id}")
        public UserDetails getUser(
                @PathVariable("id") String userId
        ) throws NotFoundException {
            return userService.getUserDetails(userId);
        }

    @RestControllerAdvice(basePackages = "path-to-v1-controller-package")
    @Log4j2
    public class V1APIExceptionHandler extends ResponseEntityExceptionHandler {
        
        @ExceptionHandler(NotFoundException.class)
        @ResponseStatus(HttpStatus.NOT_FOUND)
        public V1ErrorResponse handleNotFoundException(NotFoundException ex) {
                return V1ErrorResponse.builder().error(ex.getMessage()).build();
            }
        }
    
    
    @RestControllerAdvice(basePackages = "path-to-v1-controller-package")
    @Log4j2
    public class V2APIExceptionHandler extends ResponseEntityExceptionHandler {
        
        @ExceptionHandler(NotFoundException.class)
        @ResponseStatus(HttpStatus.NOT_FOUND)
        public V2ErrorResponse handleNotFoundException(NotFoundException ex) {
                return V2ErrorResponse.builder().error(ex.getMessage()).build();
            }
        }
  • I hope the v2 mapping `v1/user/{id}` is a typo and it is really `v2/user/{id}`. Sorry, typed too fast...it must be a typo because Spring definitely does not like duplicate mappings! – Paul Jul 30 '22 at 21:10
  • My bad. It's a typo @Paul – Shazman Malik Jul 31 '22 at 14:03