为缺少的标头拦截 @RequestHeader 异常

Posted

技术标签:

【中文标题】为缺少的标头拦截 @RequestHeader 异常【英文标题】:Intercept @RequestHeader exception for missing header 【发布时间】:2014-09-28 20:24:14 【问题描述】:

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

@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。 有什么想法吗?

【问题讨论】:

【参考方案1】:

在 Spring 5+ 中就是这么简单。 ErrorResponse 是你自己要返回的对象

@RestControllerAdvice
public class ControllerExceptionHandler 

@ExceptionHandler(MissingRequestHeaderException.class)
public ResponseEntity<ErrorResponse> handleException(MissingRequestHeaderException ex) 
    log.error("Error due to: " + ex.getMessage());

    ErrorResponse errorResponse = new ErrorResponse();

    return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);

【讨论】:

【参考方案2】:

您可以创建自定义异常类,例如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”

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

提供的请求标头无效。

【讨论】:

【参考方案3】:

您可以在此请求参数中添加@Nullable,如果不存在,请求仍然进入控制器而不抛出MissingRequestHeaderException,并且您添加手动验证以在控制器中抛出您喜欢的任何内容并在ExceptionHandler中处理。

【讨论】:

【参考方案4】:

如果 Spring 版本为 5+,那么您需要处理的确切异常是 MissingRequestHeaderException。如果您的全局异常处理程序类扩展了ResponseEntityExceptionHandler,那么为ServletRequestBindingException 添加@ExceptionHandler 将不起作用,因为MissingRequestHeaderException 扩展了ServletRequestBindingException,而后者在handleExceptionResponseEntityExceptionHandler 方法中处理。如果你尝试,你会得到Ambiguous @ExceptionHandler method mapped for ... 异常。

【讨论】:

你在这里救了我的命...非常感谢。【参考方案5】:

你也可以在不扩展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.
    

【讨论】:

【参考方案6】:

您也可以通过使用 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 类型的方法参数缺少请求标头“授权”

和这个异常一样,您可以自定义所有其他异常。

【讨论】:

【参考方案7】:

使用注解@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;

【讨论】:

【参考方案8】:

这个比较简单。声明两种处理程序方法,一种在@RequestMappingheaders 属性中声明适当的标头,另一种不声明。 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

【讨论】:

【参考方案9】:

您应该使用@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
    

【讨论】:

是的,我选择了这个解决方案,几乎没有修改,但在你写下来之前:)【参考方案10】:

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

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,或使用其他一些现有异常。

【讨论】:

【参考方案11】:

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

首先使用@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) 我需要这个标题,所以我不能做非必需的。 2)这正是我想要的,但以更一般的方式,对于方法的数量。我认为有更干净的方法来完成这项工作,然后复制/粘贴“if(ETag == null)”。 嗨@shazin。是否可以在某处的基类中捕获此@request 标头并在其他任何地方以单个方法访问?到处写这段代码似乎效率低下 @RuslanIslamov 将 required 设置为 false 并不是说​​您不需要它,它只是让方法在不存在时不会抛出异常。您仍然可以检查该值并检查它是否为空,然后在调用省略它时按照通常的方式继续。

以上是关于为缺少的标头拦截 @RequestHeader 异常的主要内容,如果未能解决你的问题,请参考以下文章

在ajax请求后台时在请求标头RequestHeader加token

角度为7的HTTP拦截器不添加标头

transformResponse 标头参数 ($resource) 中缺少自定义响应的标头

使用 WCF 的 Http 请求中缺少授权标头

Spring Rest API 拦截器在每个/每个请求上添加响应标头

Ajax,请求标头 UTF-8 到 ISO 字符集 [重复]