1

I'm trying to migrate our manually writen OpenAPI (swagger) to a generated OpenAPI using springdoc-openapi for our Spring-Boot application. We got some issues, because the controller responses (mostly ErrorCodes) didn't match to the documentatation.

We already used a @ControllerAdvice annotated handler configuration. Here a snippet:

@ControllerAdvice
public class ExceptionHandler {

    @ResponseStatus(code = HttpStatus.NOT_FOUND)
    @ApiResponse(responseCode = "404", description = "(NOT FOUND) Resource does not exist!", content = @Content)
    @ExceptionHandler(NotFoundException.class)
    public void handleException(NotFoundException e) {
        log.warn("Returning {} due to a NotFoundException: {}", HttpStatus.NOT_FOUND, e.toString());
    }

    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ApiResponse(responseCode = "400", description = "(BAD REQUEST) Given resource is invalid!", content = @Content)
    @ExceptionHandler(InvalidResourceException.class)
    public void handleException(InvalidResourceExceptione) {
        log.error("Invalid resource: {}", e.toString());
    }

The generated API now showed all defined ApiResponses as responses for all controllers and endpoints. So I splittet the handler config using @ControllerAdvice(basePackageClasses = MyController.class) to group the possible exceptions. But there are still responses that are not fitting to all endpoints of a controller. Like:

@RestController
public class MyController {

    @ResponseStatus(HttpStatus.CREATED)
    @Operation(summary = "Create", description = "Create myResource!")
    @PostMapping(value = "/myResources/", produces = {"application/json"})
    @ResponseBody
    public Integer create(@RequestBody MyResource newResource) throws InvalidResourceException {
        return creationService.createResource(newResource).getId();
    }

    @ResponseStatus(HttpStatus.OK)
    @Operation(summary = "Update", description = "Update myResource!")
    @PutMapping(value = "/myResources/{id}", produces = {"application/json"})
    public void update(@PathVariable("id") Integer id, @RequestBody MyResource newResource)
            throws ResourceNotFoundException, InvalidResourceException {
        return updateService.updateResource(id, newResource);
    }

    @ResponseStatus(HttpStatus.OK)
    @Operation(summary = "Get", description = "Get myResource!")
    @GetMapping(value = "/myResources/{id}", produces = {"application/json"})
    @ResponseBody
    public MyResource get(@PathVariable("id") Integer id) throws ResourceNotFoundException {
        return loadingService.getResource(id);
    }
}

POST will never respond with my 'business' 404 and GET will never respond with my 'business' 400. Is it possible to annotate an endpoint, so that not possible response codes are hidden in the API?

I tried to override the responses, but didn't work as intended:

    @ResponseStatus(HttpStatus.OK)
    @Operation(summary = "Get", description = "Get myResource!")
    @ApiResponses({@ApiResponse(responseCode = "200", description = "(OK) Returning myResource"),
            @ApiResponse(responseCode = "404", description = "(NOT FOUND) Resource does not exist!")})
    @GetMapping(value = "/myResources/{id}", produces = {"application/json"})
    @ResponseBody
    public MyResource get(@PathVariable("id") Integer id) throws ResourceNotFoundException {
        return loadingService.getResource(id);
    }

400 still shows up...

MSX
  • 46
  • 5

1 Answers1

0

You need to remove the @ApiResponse from your @ControllerAdvice class and need to add the respective response in your controller class, as mentioned by you.

@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Get", description = "Get myResource!")
@ApiResponses({@ApiResponse(responseCode = "200", description = "(OK) Returning myResource"),
@ApiResponse(responseCode = "404", description = "(NOT FOUND) Resource does not exist!")})
@GetMapping(value = "/myResources/{id}", produces = {"application/json"})
@ResponseBody
public MyResource get(@PathVariable("id") Integer id) throws ResourceNotFoundException {
        return loadingService.getResource(id);
}
SSK
  • 3,444
  • 6
  • 32
  • 59
  • OK, that works, if I also add the `@hidden` annotation to the `@ExceptionHandler` methods (else I still got generic response describtions in the generated API). But I got controllers that contain about 5 endpoints which can return my business 404 or 400 and one which does not. This solution forces me to write my default response 5 time. I would like to exclude only the one endpoint, that can not respond with my default response, to prevent duplicate code. – MSX Apr 01 '21 at 09:49
  • I know this question is old, but you can create a composed custom annotation and just use the annotation on each controller endpoint. ```java @ApiResponse( responseCode = "404", content = { @Content( mediaType = "application/json", examples = @ExampleObject("blabla") ) }) @Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ApiResponseResourceNotFound {} ``` – Deewens Jul 22 '22 at 11:13
  • is there a way to keep the @RestControllerAdvice enabled on only on specific endpoints like GET that don't have 409 CONFLICT say '@ApiResponse(responseCode = "409", hidden=true ...)'? – la00 Oct 21 '22 at 09:56