【问题标题】:Spring boot ExceptionHandler when an exception is thrown within a service服务中抛出异常时的Spring Boot ExceptionHandler
【发布时间】:2020-06-02 17:02:32
【问题描述】:

我正在使用 ControllerAdvice 来处理我的 Spring Boot 应用程序中的异常。

@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class ErrorApiHandler extends ResponseEntityExceptionHandler {

    final
    ResponsesHelper rh;

    public ErrorApiHandler(ResponsesHelper rh) {
        this.rh = rh;
    }

    @ExceptionHandler(UsernameNotFoundException.class)
    public ResponseEntity<Object> handleUsernameNotFoundException(UsernameNotFoundException ex) {
    log.error(ExceptionUtils.getStackTrace(ex));

    var error = buildError(ex);
    return rh.buildResponse(error, HttpStatus.NOT_FOUND);
    }
...
}

它适用于我的控制器中引发的异常。 但是,如果抛出异常,例如在服务中,ControllerAdvice 不会被执行。

@Service
public class CustomUserDetailsService implements UserDetailsService {

    final
    UserRepository userRepository;

    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    @Transactional
    public User loadUserByUsername(String email)
            throws UsernameNotFoundException {

        log.debug(String.format("Loading user %s", email));

        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> {

                    log.debug(String.format("User %s not found", email));
                    return new UsernameNotFoundException("User not found : " + email); // <- This exception is not handled.
                });

        log.debug(String.format("User %s loaded", user));
        return user;
    }

如何处理我的应用程序中抛出的所有异常?

提前致谢。

【问题讨论】:

标签: java spring spring-boot exception


【解决方案1】:

我在 ResponseEntityExceptionHandler 文档中找到了这个:

@ControllerAdvice 类的便捷基类,希望通过 @ExceptionHandler 方法在所有 @RequestMapping 方法中提供集中式异常处理。

似乎扩展该类的自定义异常处理程序只会处理控制器层中的异常。

我找到了this tutorial - 一个使用 HandlerExceptionResolver 的解决方案听起来像您正在寻找的解决方案。

【讨论】:

    【解决方案2】:

    @ControllerAdvice 旨在处理通过控制器方法传播的异常(从控制器方法调用中抛出的异常 - 包括冒泡异常)。因此,每当您直接在控制器方法中抛出异常或控制器方法正在调用的东西抛出异常时,都会尝试通过通知处理它。

    如果您希望以类似的方式在 Web 上下文之外(以某种方式)处理您的异常,您将必须编写自己的切面,该切面将真正包装所有 try-catch 并让您处理异常。

    【讨论】:

      【解决方案3】:

      在我的例子中,我的“CustomUserDetailsS​​ervice”在过滤器中被调用:

      public class JwtAuthenticationFilter extends OncePerRequestFilter {
      
          @Autowired
          private JwtTokenProvider tokenProvider;
      
          @Autowired
          private CustomUserDetailsService customUserDetailsService;
      
          @Override
          protected void doFilterInternal(
                  HttpServletRequest request,
                  HttpServletResponse response,
                  FilterChain filterChain
              ) throws ServletException, IOException {
      
              try {
                  String jwt = getJwtFromRequest(request);
      
                  if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
                      UUID userId = tokenProvider.getUserIdFromJWT(jwt);
      
                      UserDetails userDetails = customUserDetailsService.loadUserById(userId);
      ...
      

      我通过捕捉那里的异常来处理它:

      catch (UsernameNotFoundException ex){
                  log.error(ExceptionUtils.getStackTrace(ex));
      
                  var apiError = ErrorApiHandler.buildError(
                          new ResourceAlreadyTakenException(ex.getMessage())
                  );
      
                  response.addHeader("Content-Type", "application/json");
                  response.setStatus(HttpStatus.BAD_REQUEST.value());
                  PrintWriter writer = response.getWriter();
                  writer.write(convertObjectToJson(apiError));
                  writer.flush();
                  return;
              }
      

      理想情况下,使用控制器建议处理所有异常,但这样可以正常工作

      【讨论】:

        猜你喜欢
        • 2021-12-24
        • 1970-01-01
        • 1970-01-01
        • 2019-09-06
        • 1970-01-01
        • 2012-03-06
        • 2020-02-05
        • 2018-12-06
        • 2016-09-26
        相关资源
        最近更新 更多