春季靴子休息以空的身体响应,除了在我的@ControllerAdvice中覆盖的例外情况

我有一个@ControllerAdvice扩展ResponserEntityExceptionHandler,作为我试图控制API调用工作流中引发的任何异常的标准响应。

没有控制器建议。我得到了由spring生成的基于HTML的通用响应,其中包含正确的响应标头。但是当我添加我的@ControllerAdvice时,Spring不会使用通用错误正文进行响应。正文为空,具有正确的响应标头

@Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
        HttpHeaders headers, HttpStatus status, WebRequest request) {

        String erroMessage = "Required Parameter: '"+ex.getParameterName()+"' was not available in the request.";
        TrsApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, erroMessage, ex, ApiErrorCode.INVALID_REQUEST);
        return buildResponseEntity(apiError);
}

因此,现在,如果请求中缺少必需的参数,流可以很好地触发我的重写实现,并使用描述错误的JSON有效负载进行响应。但是,如果出现任何其他异常,如HttpMediaTypeNotAcceptableException,spring正在以空体响应。

在我添加建议之前,spring正在以通用错误响应进行响应。我是春季靴子生态系统的新手。需要帮助来了解这是否是实现集中式错误处理的更好方法的预期行为。


答案 1

我猜我在 ControllerAdvice 类扩展时发现了吞咽身体的解决方案。在我的情况下,设置如下所示:ResponeEntityExceptionHandler

@ControllerAdvice
@Slf4j
class GlobalExceptionHandlers extends ResponseEntityExceptionHandler {

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
                                      MethodArgumentNotValidException exception,
                                      HttpHeaders headers,
                                      HttpStatus status,
                                      WebRequest request) {
        // logic that creates apiError object (object with status, message, errorCode, etc)
        //...
        return handleExceptionInternal(exception, apiError, headers, status, request);
    }

这就像是类例外的魅力。但它破坏了 由 处理的所有其他异常,并为它们返回空的响应正文。MethodArgumentNotValidExceptionResponseEntityExceptionHandler

但是修复很容易,只需从中覆盖:handleExceptionInternalResponseEntityExceptionHandler

@ControllerAdvice
@Slf4j
class GlobalExceptionHandlers extends ResponseEntityExceptionHandler {

    /// ... code from previous snippet

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(
                                      Exception exception, 
                                      Object body, 
                                      HttpHeaders headers, 
                                      HttpStatus status, 
                                      WebRequest request) {
        // for all exceptions that are not overriden, the body is null, so we can
        // just provide new body based on error message and call super method
        var apiError = Objects.isNull(body) 
                ? new ApiError(status, exception.getMessage()) // <-- 
                : body;
        return super.handleExceptionInternal(exception, apiError, headers, status, request);
    }
}

答案 2

这是预期的行为。查看类 ResponseEntityExceptionHandler 的源代码。

@ExceptionHandler({
            org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException.class,
            HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class,
            HttpMediaTypeNotAcceptableException.class,
            MissingPathVariableException.class,
            MissingServletRequestParameterException.class,
            ServletRequestBindingException.class,
            ConversionNotSupportedException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
            HttpMessageNotWritableException.class,
            MethodArgumentNotValidException.class,
            MissingServletRequestPartException.class,
            BindException.class,
            NoHandlerFoundException.class,
            AsyncRequestTimeoutException.class
        })
    public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) {

所有这些异常都是在没有响应正文的情况下处理的。调用一个常用方法:

//second parameter is body which is null
handleExceptionInternal(ex, null, headers, status, request)

如果您需要以不同的方式处理特定的异常,请覆盖它们,例如,我想为HttpMessageNotReadableException发送自定义响应

 @Override
    protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
        HttpHeaders headers, HttpStatus status, WebRequest request)
    {
        logger.error("handleHttpMessageNotReadable()", ex);
        ValidationErrors validationErrors = null;
        if (ex.getRootCause() instanceof InvalidFormatException) {
            InvalidFormatException jacksonDataBindInvalidFormatException = (InvalidFormatException) ex.getRootCause();
            validationErrors = new ValidationErrors(jacksonDataBindInvalidFormatException.getOriginalMessage());
        }
        headers.add("X-Validation-Failure", "Request validation failed !");
        return handleExceptionInternal(ex, validationErrors, headers, status, request);
    }

推荐