I need to document my SpringBoot APIs and their possible exceptions with OpenAPI, and I am using SpringDoc-OpenAPI https://springdoc.org/.
To handle the NotFound cases, I created this exception class:
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
public class NotFoundException extends ResponseStatusException {
public NotFoundException() {
super(HttpStatus.NOT_FOUND);
}
}
and this @RestControllerAdvice
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalControllerExceptionHandler {
@ExceptionHandler(NotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity<String> handleNotFoundException(RuntimeException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
The problem I am facing is that the generated OpenAPI yaml file has
responses:
"404":
description: Not Found
content:
'*/*':
schema:
type: string
for all @RestController
endpoints, instead of only for the methods with throws NotFoundException
.
How can I limit the @ControllerAdvice (or the OpenAPI), to generate the 404 Response documentation only for methods with the throwing signature?
Do I need to use something else other than the @RestControllerAdvice? I would like to avoid having to annotate every single method.
A possible solution is to:
@RestControllerAdvice
@Hidden
OperationCustomizer
@Bean
import io.swagger.v3.oas.annotations.Hidden;
import it.eng.cysec.ot.risk.assessment.api.exceptions.NotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Hidden
@RestControllerAdvice
public class GlobalControllerExceptionHandler {
@ExceptionHandler(NotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity<String> handleNotFoundException(NotFoundException exception) {
return new ResponseEntity<>(exception.getMessage(), HttpStatus.NOT_FOUND);
}
}
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import it.eng.cysec.ot.risk.assessment.api.exceptions.NotFoundException;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springframework.web.method.HandlerMethod;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
public class OperationResponseCustomizer implements OperationCustomizer {
public static final ApiResponse NOT_FOUND_API_RESPONSE;
static {
MediaType mediaType = new MediaType();
mediaType.setSchema(new StringSchema());
Content content = new Content();
content.addMediaType("*/*", mediaType);
NOT_FOUND_API_RESPONSE = new ApiResponse()
.description("Not Found")
.content(content);
}
/**
* Customize operation.
*
* @param operation input operation
* @param handlerMethod original handler method
* @return customized operation
*/
@Override
public Operation customize(Operation operation, HandlerMethod handlerMethod) {
Method method = handlerMethod.getMethod();
List<Class<?>> exceptions = Arrays.asList(method.getExceptionTypes());
if(exceptions.contains(NotFoundException.class)){
ApiResponses apiResponses = operation.getResponses();
apiResponses.addApiResponse("404", NOT_FOUND_API_RESPONSE);
}
return operation;
}
}