【问题标题】:Is there a simpler exception handling technique for Spring?Spring 是否有更简单的异常处理技术?
【发布时间】:2018-12-03 03:31:51
【问题描述】:

我已经阅读了有关使用 @ExceptionHandler 的基于控制器的异常。

我已阅读有关使用 @ControllerAdvice 进行全局异常处理的文章。

我还阅读了有关扩展 HandlerExceptionResolver 以进行更深入的异常处理的文章。

但是,我最理想的做法是能够在我的应用程序的任何层使用指示返回给客户端的 JSON 响应的参数引发全局异常。

例如:

throw new CustomGlobalException(HttpStatus.UNAUTHORISED, "This JWT Token is not Authorised.")

throw new CustomGlobalException(HttpStatus.FORBIDDEN, "This JWT Token is not valid.")

然后,这将根据我创建的模型以及状态返回 JSON 响应,例如:

{
    "success" : "false",
    "message" : "This JWT Token is not Authorised."
} 

为此,我的控制器将其作为 REST 响应返回。 这样的事情可能吗?或者我是否必须按照文档中的说明对所有内容进行自定义错误异常处理。

为了澄清,我要求异常中断正在进行的进程,可能从数据库中获取数据,并立即将给定的异常返回给客户端。我有一个 web mvc 设置。


更多细节:

 @ControllerAdvice
 @RequestMapping(produces = "application/json")
public class GlobalExceptionHandler {

@ExceptionHandler(CustomException.class)
public ResponseEntity<Object> handleCustomException(CustomException ex,
                                                    WebRequest request) {
    Map<String, Object> response = new HashMap<>();

    response.put("message", ex.getMessage());
    return new ResponseEntity<>(response, ex.getCode());
}
}

这里抛出异常:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain
        filterChain) throws ServletException, IOException {

    logger.debug("Filtering request for JWT header verification");

    try {
        String jwt = getJwtFromRequest(request);

        logger.debug("JWT Value: {}", jwt);

        if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
            String username = tokenProvider.getUserIdFromJWT(jwt);

            UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken
                    (userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

            SecurityContextHolder.getContext().setAuthentication(authentication);
        } else {
            logger.error("No Valid JWT Token Provided");
                            throw new CustomException(HttpStatus.UNAUTHORIZED, "No Valid JWT Token Provided");
        }
    } catch (Exception ex) {
        logger.error("Could not set user authentication in security context", ex);
    }

    filterChain.doFilter(request, response);
}

【问题讨论】:

    标签: spring spring-mvc spring-boot spring-security spring-data-jpa


    【解决方案1】:

    从 Spring 5 及更高版本开始,ResponseStatusException(提供 spring 框架)会更好。 请参考spring-response-status-exception

    【讨论】:

      【解决方案2】:

      按照我关于如何处理异常here 的帖子,您可以编写自己的处理程序,如下所示,

      class CustomGlobalException {
          String message;
          HttpStatus status;
      }
      
      @ExceptionHandler(CustomGlobalException.class)
      public ResponseEntity<Object> handleCustomException(CustomGlobalException ex,
                  WebRequest request) {
          Map<String, Object> response = new HashMap<>();
      
          response.put("success", "false");
          response.put("message", ex.getMessage());
      
          return new ResponseEntity<>(response, ex.getStatus());
      }
      

      上面提到的代码会处理CustomGlobalException发生的任何一层代码。

      【讨论】:

      • 这是理想的,但它不会被退回给客户端。换句话说,如果我设置它并抛出一个 CustomGlobalException,信息将被记录,但函数的其余部分将继续执行。
      • > 函数的其余部分将继续执行。你能详细说明一下吗?
      • 没有。我提供的方式不会执行其余代码。在这种情况下,方法执行将停止并且错误将返回给客户端。如果出现错误,用户将看到 return new ResponseEntity(response, ex.getStatus()); 的 o/p。注意:我在我的项目中使用的这种处理异常的方式,它就像我所说的那样工作。如果它不起作用,请删除您正在尝试的代码。我很想看看..
      • 好的。我签入了我的项目。当我需要验证客户端凭据时,感染也有类似的情况。正如我解释的那样,它正在工作。我看到了你的代码。我要求您进行以下更改并再次检查。如下所述更改您的 GlobalExceptionHandler。 @RestControllerAdvice @RequestMapping(produces = "application/json") public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { } 并在 @ExceptionHandler(CustomGlobalException.class) 中使用 CustomException (在给定的代码 sn -p)
      • 我已经为您创建了演示项目,并且它按预期工作。这是一个链接github.com/shauank/spring-boot/tree/master/demo
      【解决方案3】:

      这并不能完全实现您想要实现的目标,但是几乎可以实现您想要的最简单的方法(并且更简洁,IMO)是简单地定义一个异常,如下所示:

      @ResponseStatus(HttpStatus.UNAUTHORIZED)
      public class UnauthorizedException extends RuntimeException {
          public UnauthorisedException(String message) {
              super(message);
          }
      }
      

      现在每次从控制器方法(直接或间接)抛出(不返回)这样的异常时,您都会得到这样的响应

      {
          "timestamp": "2018-06-24T09:38:51.453+0000",
          "status": 401,
          "error": "Unauthorized",
          "message": "This JWT Token is not Authorised.",
          "path": "/api/blabla"
      }
      

      当然,HTTP 响应的实际状态码也是 401。

      您也可以抛出ResponseStatusException,它更通用,允许您使用相同的异常类型并将状态作为参数传递。但我觉得它不太干净。

      【讨论】:

      • 感谢您的回复。我也同意,我不喜欢将状态作为参数传递而让实际的错误异常停滞不前。我会调查一下,谢谢!
      • 我遇到了同样的问题,也许我不清楚。我想在我的代码中的任何时候抛出一个 UnauthorizedException,并且当前进程被中断并且异常被立即返回给客户端。然而这也不会导致,我只是抛出一个异常然后它继续执行。
      • 如果它继续执行,这意味着你正在以某种方式捕获异常。不要,让异常冒泡,直到 Spring 捕获它并将上面的 JSON 发送到客户端。
      猜你喜欢
      • 2012-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-03
      • 2011-01-19
      • 1970-01-01
      • 2011-07-09
      • 1970-01-01
      相关资源
      最近更新 更多