【问题标题】:spring rest Handling empty request body (400 Bad Request)spring rest 处理空请求体(400 Bad Request)
【发布时间】:2017-05-08 15:33:47
【问题描述】:

我正在使用 Spring4 开发一个 RESTful 应用程序。 我想处理 POST 请求不包含正文的情况。 我编写了以下自定义异常处理程序:

    @ControllerAdvice
    public class MyRestExceptionHandler {
     
      @ExceptionHandler
      @ResponseStatus(HttpStatus.BAD_REQUEST)
      public ResponseEntity<MyErrorResponse> handleJsonMappingException(JsonMappingException ex) {
          MyErrorResponse errorResponse = new MyErrorResponse("request has empty body");
          return new ResponseEntity<MyErrorResponse>(errorResponse, HttpStatus.BAD_REQUEST);
      }   
      @ExceptionHandler(Throwable.class)
      public ResponseEntity<MyErrorResponse> handleDefaultException(Throwable ex) {
        MyErrorResponse errorResponse = new MyErrorResponse(ex);
        return new ResponseEntity<MyErrorResponse>(errorResponse, HttpStatus.BAD_REQUEST);
      }
    }
    
     @RestController
     public class ContactRestController{
        @RequestMapping(path="/contact", method=RequestMethod.POST)
        public void save(@RequestBody ContactDTO contactDto) {...}
     } 

当它收到没有正文的 POST 时,不会调用这些方法。 反而, 客户端收到 400 BAD REQUEST HTTP 状态和空正文的响应。 有人知道怎么处理吗?

【问题讨论】:

  • 你试过@ExceptionHandler(JsonMappingException.class)吗?
  • 是的。同样的行为。

标签: java spring rest spring-boot spring-4


【解决方案1】:

我解决了这个问题 (自定义异常处理程序必须扩展 ResponseEntityExceptionHandler)。 我的解决方案如下:

        @ControllerAdvice
        public class RestExceptionHandler extends ResponseEntityExceptionHandler {
    
            @Override
            protected ResponseEntity<Object> handleHttpMessageNotReadable(
                HttpMessageNotReadableException ex, HttpHeaders headers,
                HttpStatus status, WebRequest request) {
                // paste custom hadling here
            }
        }

【讨论】:

【解决方案2】:

就我而言,我需要处理所有具有无效参数的请求。所以我用ResponseEntityExceptionHandler 扩展了我的类并覆盖了handleMissingServletRequestParameter 方法。您可以在 ResponseEntityExceptionHandler 类中找到自己定义的处理程序

@ControllerAdvice 
public class YourExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(Exception.class)
    public final ResponseEntity handleAllExceptions(Exception ex) {
        // Log and return
    }

    @Override
    public ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        // Do your code here
        return new ResponseEntity<>("YOUR REQUEST PARAMS NOT MATCH!");
    } 
}

【讨论】:

    【解决方案3】:

    我遇到了类似的问题,但它对我不起作用,因为提供的 component-scanpackage 不包括提供我的 @ControllerAdvice 的包。

    我的 XML 有:

    <context:component-scan base-package="com.bandi.rest" />
    

    我的包裹有一个错字com.bandi.test.spring.exception。将其更改为com.bandi.rest.spring.exception 后,它开始工作。

    @ControllerAdvice
    public class SpringRestExceptionHandler {
    
        @ExceptionHandler(NoHandlerFoundException.class)
        @ResponseStatus(HttpStatus.NOT_FOUND)
        public @ResponseBody ResponseEntity<ErrorResponse> handleNoMethodException(HttpServletRequest request,
                NoHandlerFoundException ex) {
            ErrorResponse errorResponse = new ErrorResponse(ex);
            errorResponse.setErrorMessage("resource not found with exception");
            return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND);
        }
    
        @ExceptionHandler(Throwable.class)
        public @ResponseBody ResponseEntity<ErrorResponse> handleDefaultException(Throwable ex) {
            ErrorResponse errorResponse = new ErrorResponse(ex);
            errorResponse.setErrorMessage("request has empty body  or exception occured");
            return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.BAD_REQUEST);
        }
    }
    

    此外,如果您需要处理未找到您请求的资源(错误 URL)的情况,那么您必须向调度程序 servlet 添加另一个配置。

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>throwExceptionIfNoHandlerFound</param-name>
            <param-value>true</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
    

    完整的工作代码可用here

    【讨论】:

    • 我使用的是 Spring Boot,所以我不需要做额外的 xml 配置。Spring 会找到所有带注释的 bean。我稍后会检查您的示例
    • 可能你需要添加基于注释的配置。正如here 提到的,您还可以验证您的@ComponentScan 注释是否包含ExceptionHandler 的包?
    • 一般我的 ExeptionHandler 正在工作。处理主题中的所有错误。甚至 NoHadlerFound 例外。我正在使用this 建议来启用它。
    • 终于找到了解决办法。
    • 你能发布你找到的解决方案吗?
    【解决方案4】:

    如果您已经有一个用@ControllerAdvice 注释的类并且不想创建新的,您可以使用这段代码:

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<?> handleMissingRequestBody(Exception ex) {
        return handle(BAD_REQUEST, ex);
    }
    

    它应该与 rvit34 的解决方案具有相同的行为。

    【讨论】:

      【解决方案5】:

      我也遇到过同样的问题,在花了很多时间调试问题后,我发现 jackson 没有正确反序列化 ErrorResponse 对象。

      这是因为我没有为ErrorResponse 对象中定义的字段添加getter 和setter。我正在使用构造函数初始化字段,并且没有为这些字段定义 getter 和 setter。

      解决方案:

      所以当我从

      更新我的ErrorResponse 对象时
      import com.fasterxml.jackson.annotation.JsonRootName;
      import java.util.List;
      
      @JsonRootName("error")
      public class ErrorResponse {
      
        private String message;
        private List<String> details;
      
        public ErrorResponse(String message, List<String> details) {
          this.message = message;
          this.details = details;
        }
      }
      

      下面的getter和setter方法

      import com.fasterxml.jackson.annotation.JsonRootName;
      import java.util.List;
      
      @JsonRootName("error")
      public class ErrorResponse {
      
        private String message;
        private List<String> details;
      
        public ErrorResponse(String message, List<String> details) {
          this.message = message;
          this.details = details;
        }
      
        public String getMessage() {
          return message;
        }
      
        public void setMessage(String message) {
          this.message = message;
        }
      
        public List<String> getDetails() {
          return details;
        }
      
        public void setDetails(List<String> details) {
          this.details = details;
        }
      }
      

      Jackson 现在正在正确反序列化 ErrorResponse,我在响应中得到序列化的正文。

      【讨论】:

        【解决方案6】:

        在控制器类中,我提出了以下方法,它解决了我的问题。无需控制器建议或任何其他。只需使用 body 本身的用户异常覆盖 spring 默认异常即可解决问题。

        @ResponseStatus(value = HttpStatus.BAD_REQUEST)
            @ExceptionHandler(value = {MissingServletRequestParameterException.class})
            ApiError handleMethodArgumentNotValid(MissingServletRequestParameterException ex) {
        
                return new ApiError(ErrorCode.MISSING_REQUIRED_PARAMS, ex.getMessage());
            }
        

        【讨论】:

          【解决方案7】:

          override handleExceptionInternal 方法处理所有异常,因此您不需要覆盖每个处理方法:

              @Override
              protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, 
          HttpStatus status, WebRequest request) {
                  MyErrorResponse myErrorResponse = new MyErrorResponse();
                  MyErrorResponse.setMessage(ex.getMessage());
                  return new ResponseEntity<>(myErrorResponse, status);
              }
          

          【讨论】:

            【解决方案8】:
                @ControllerAdvice
                @ResponseBody
                public class ControllerExceptionHandler {
            
                @ExceptionHandler(BadCredentialsException.class)
                @ResponseStatus(value = HttpStatus.FORBIDDEN)
                public BaseResponseBean badCredentialsException(BadCredentialsException ex, WebRequest request) {
                    BaseResponseBean responseBean = new BaseResponseBean();
                    StatusBean status = new StatusBean();
                    status.setEnErrorMessage(ex.getMessage());
                    status.setCode(403);
                    status.setSuccess(false);
                    responseBean.setStatus(status);
                    return responseBean;
                }
                @ExceptionHandler(HttpClientErrorException.BadRequest.class)
                @ResponseStatus(value = HttpStatus.BAD_REQUEST)
                public BaseResponseBean badRequestException(HttpClientErrorException.BadRequest ex, WebRequest request) {
                    BaseResponseBean responseBean = new BaseResponseBean();
                    StatusBean status = new StatusBean();
                    status.setEnErrorMessage(ex.getMessage());
                    status.setCode(400);
                    status.setSuccess(false);
                    responseBean.setStatus(status);
                    return responseBean;
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2021-12-21
              • 2018-03-13
              • 2017-09-26
              • 1970-01-01
              • 2020-05-29
              • 2013-10-02
              • 2019-10-22
              • 2021-07-20
              • 1970-01-01
              相关资源
              最近更新 更多