Spring-Boot OpenAPI - @RestControllerAdvice 不限于抛出异常的方法

Spring-Boot OpenAPI - @RestControllerAdvice not limited to methods throwing the Exception

我需要用 OpenAPI 记录我的 SpringBoot API 及其可能的异常, 我正在使用 SpringDoc-OpenAPI https://springdoc.org/.

为了处理 NotFound 案例,我创建了这个异常 class:

import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;

public class NotFoundException extends ResponseStatusException {
    public NotFoundException() {
        super(HttpStatus.NOT_FOUND);
    }
}

和这个@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);
    }
}

我遇到的问题是生成的OpenAPI yaml文件有

  responses:
    "404":
      description: Not Found
      content:
        '*/*':
          schema:
            type: string

适用于所有 @RestController 个端点,而不是仅适用于具有 throws NotFoundException.

的方法

我如何限制@ControllerAdvice(或 OpenAPI),以便仅为具有 throwing 签名的方法生成 404 响应文档?

我需要使用@RestControllerAdvice 以外的其他东西吗? 我想避免必须注释每个方法。

一个可能的解决方案是:

  1. 制作 @RestControllerAdvice @Hidden
  2. 提供 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;
    }
}