拦截缺少标头的@RequestHeader异常

时间:2014-08-06 02:14:55

标签: java spring spring-mvc servlets

我在控制器中有一个带参数的方法,例如

@RequestMapping(value = "/{blabla}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void post(@RequestHeader("ETag") int etag)

如果请求中没有ETag标头 - 客户端获得400(BAD_REQUEST),这不是任何信息。 我需要以某种方式处理此异常并将我自己的异常发送到客户端(我为此目的使用JSON)。 我知道我可以通过@ExceptionHandler拦截异常,但在这种情况下,所有HTTP 400请求都将被处理,但我希望在标题中缺少ETag。 有什么想法吗?

10 个答案:

答案 0 :(得分:6)

有两种方法可以实现您的目标

首先将@RequestHeaderrequired false

一起使用
@RequestMapping(value = "/{blabla}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void post(@RequestHeader(value="ETag", required=false) String ETag) {
    if(ETag == null) {
        // Your JSON Error Handling
    } else {
        // Your Processing
    }
}

第二次使用HttpServletRequest代替@RequestHeader

@RequestMapping(value = "/{blabla}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void post(HttpServletRequest request) {
    String ETag = request.getHeader("ETag");
    if(ETag == null) {
        // Your JSON Error Handling
    } else {
        // Your Processing
    }
}

答案 1 :(得分:3)

如果Spring版本是5+,那么您需要处理的确切异常是MissingRequestHeaderException。如果您的全局异常处理程序类扩展了ResponseEntityExceptionHandler,则无法为@ExceptionHandler添加ServletRequestBindingException,因为MissingRequestHeaderException扩展了ServletRequestBindingException并且后者在{ handleException的{​​1}}方法。如果您尝试尝试,将获得ResponseEntityExceptionHandler例外。

答案 2 :(得分:2)

使用注释@ExceptionHandler编写一个方法并使用ServletRequestBindingException.class,因为在缺少标题的情况下抛出此异常

例如:

@ExceptionHandler(ServletRequestBindingException.class)
public ResponseEntity<ResponseObject> handleHeaderError(){

    ResponseObject responseObject=new ResponseObject();
    responseObject.setStatus(Constants.ResponseStatus.FAILURE.getStatus());
    responseObject.setMessage(header_missing_message);

    ResponseEntity<ResponseObject> responseEntity=new ResponseEntity<ResponseObject>(responseObject, HttpStatus.BAD_REQUEST);
    return responseEntity;
}

答案 3 :(得分:2)

您还可以通过使用spring的注释@ControllerAdvice来实现此目的。

@ControllerAdvice
public class ExceptionHandler extends ResponseEntityExceptionHandler{

    /**
     * Handle ServletRequestBindingException. Triggered when a 'required' request
     * header parameter is missing.
     *
     * @param ex      ServletRequestBindingException
     * @param headers HttpHeaders
     * @param status  HttpStatus
     * @param request WebRequest
     * @return the ResponseEntity object
     */
    @Override
    protected ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex,
            HttpHeaders headers, HttpStatus status, WebRequest request) {
        return new ResponseEntity<>(ex.getMessage(), headers, status);
    }

}

在没有必需请求标头的情况下访问API时的响应是:

  

缺少类型为String的方法参数的请求标头'Authorization'

像该例外一样,您可以自定义所有其他例外。

答案 4 :(得分:1)

您应该使用@ExceptionHandler方法查看是否存在ETag标头并采取适当的措施:

@ExceptionHandler(UnsatisfiedServletRequestParameterException.class)
public onErr400(@RequestHeader(value="ETag", required=false) String ETag,
        UnsatisfiedServletRequestParameterException ex) {
    if(ETag == null) {
        // Ok the problem was ETag Header : give your informational message
    } else {
        // It is another error 400  : simply say request is incorrect or use ex
    }
}

答案 5 :(得分:0)

如果您不想在请求映射中处理此问题,则可以创建Servlet过滤器并在过滤器中查找ETag标头。如果不存在,则抛出异常。这仅适用于与您的过滤器网址映射匹配的请求。

public final class MyEtagFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String etag = request.getHeader("ETag");

        if(etag == null)
            throw new MissingEtagHeaderException("...");

        filterChain.doFilter(request, response);
    }
}

您必须实现自己的MissingEtagHeaderException,或者使用其他一些现有的异常。

答案 6 :(得分:0)

这个比较简单。声明两个处理程序方法,一个在@RequestMapping headers属性中声明适当的标头,另一个不在。{ Spring会根据请求的内容注意调用适当的内容。

@RequestMapping(value = "/{blabla}", method = RequestMethod.POST, headers = "ETag")
@ResponseStatus(HttpStatus.CREATED)
public void postWith(@RequestHeader("ETag") int etag) {
    // has it
}

@RequestMapping(value = "/{blabla}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void postWithout() {
    // no dice
    // custom failure response
}

答案 7 :(得分:0)

您也可以在不扩展ResponseEntityExceptionHandler的情况下拦截异常:

@ControllerAdvice
public class ControllerExceptionHandler {

    @ExceptionHandler(ServletRequestBindingException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex) {
        // return a ResponseEntity<Object> object here.
    }
}

答案 8 :(得分:0)

您可以将@Nullable添加到此请求参数中,并且在不存在请求的情况下,请求仍会进入控制器而不会抛出MissingRequestHeaderException,并且您可以添加手动验证以将所需的内容抛出到控制器并处理ExceptionHandler。

答案 9 :(得分:0)

您可以创建自定义异常类,例如InvalidRequestHeaderException.java。您可以在此处自定义异常消息。

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class InvalidRequestHeaderException extends RuntimeException {

  public InvalidRequestHeaderException() {
    super("Invalid request header provided.");
  }
}

在您的控制器中,如果提供的标头无效,您可以抛出异常。

@RequestMapping(value = "/{blabla}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void post(@RequestHeader("ETag") int etag) {
  // some code
  
  if (!isSupportedPlatform(platform)) {
    throw new InvalidRequestHeaderException();
  }
  
  // some code
}

然后您可以创建一个 ValidationHandler.java 来处理这些异常。

@RestControllerAdvice
public class ValidationHandler extends ResponseEntityExceptionHandler {

  @ExceptionHandler(value = {
      MissingRequestHeaderException.class,
      InvalidRequestHeaderException.class
  })
  protected ResponseEntity<Object> handleRequestHeaderException(Exception ex) {
    log.error(ex.getMessage());
    return ResponseEntity.badRequest().body(ErrorResponse.builder()
        .status(String.valueOf(HttpStatus.BAD_REQUEST.value()))
        .reason(ex.getMessage()).build());
  }


  @AllArgsConstructor
  @Getter
  @Builder
  public static class ErrorResponse {

    private String status;
    private String reason;
  }
}

通过使用 MissingRequestHeaderException,如果您用 @RequestHeader 注释的内容丢失,它将抛出异常,因此您将得到如下异常:

<块引用>

缺少类型为 int 的方法参数的请求标头“Etag”

当请求头存在但无效时,将抛出此异常:

<块引用>

提供的请求标头无效。