spring rest 处理空请求体(400 Bad Request)

Posted

技术标签:

【中文标题】spring rest 处理空请求体(400 Bad Request)【英文标题】:spring rest Handling empty request body (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)吗? 是的。同样的行为。 【参考方案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
            
        

【讨论】:

它会只为 post 方法调用吗? 当结合这两个链接的解决方案时:它完全按照我想要的方式为我工作。解决方案链接:1)***.com/a/60643988 2)***.com/a/41308274【参考方案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;
    

【讨论】:

以上是关于spring rest 处理空请求体(400 Bad Request)的主要内容,如果未能解决你的问题,请参考以下文章

SharePoint REST API Online 批处理请求问题:获取 400 Bad Request, odata.error Invalid request

Extjs 6 + Spring Rest:401 错误的空响应

将 JSON 发布到控制器返回 400 错误请求

在 Spring Boot REST 应用程序中处理 gzipped 请求

spring boot 处理大请求体 - 通过多线程处理批量

这个在 Spring 中处理 POST 请求的 REST 方法究竟是如何工作的?