简体   繁体   English

在服务中抛出异常时 Spring 启动 ExceptionHandler

[英]Spring boot ExceptionHandler when an exception is thrown within a service

I am using ControllerAdvice to handle exceptions in my spring boot application.我正在使用 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);
    }
...
}

It works fine for exceptions thrown within my controllers.它适用于我的控制器中抛出的异常。 However, with exceptions thrown, for example within a service the ControllerAdvice is not executed.但是,如果抛出异常,例如在服务中,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;
    }

How can I handle all exceptions thrown within my application?如何处理应用程序中抛出的所有异常?

Thanks in advance.提前致谢。

I found this in ResponseEntityExceptionHandler docs:我在 ResponseEntityExceptionHandler 文档中找到了这个:

A convenient base class for @ControllerAdvice classes that wish to provide centralized exception handling across all @RequestMapping methods through @ExceptionHandler methods. @ControllerAdvice 类的一个方便的基类,希望通过 @ExceptionHandler 方法跨所有 @RequestMapping 方法提供集中的异常处理。

It seems that a custom exception handler that extends that class will only handle exceptions in the controller layer.似乎扩展该类的自定义异常处理程序只会处理控制器层中的异常。

I found this tutorial - a solution that uses HandlerExceptionResolver sounds like the one you are looking for.我找到了这个教程- 一个使用 HandlerExceptionResolver 的解决方案听起来像你正在寻找的那个。

@ControllerAdvice is meant to handle exceptions that propagate through controller methods (that are thrown from within controller method calls - including bubbling exceptions). @ControllerAdvice旨在处理通过控制器方法传播的异常(从控制器方法调用中抛出 - 包括冒泡异常)。 So whenever you directly throw exceptions in your controller method or something that controller method is calling throws an exception, an attempt to handle it through advice will be made.因此,每当您在控制器方法中直接抛出异常或控制器方法调用的某些内容抛出异常时,都会尝试通过通知来处理它。

If you want your exceptions to be handled (somehow) outside of web context with a similar manner, your will have to write your own aspect that will literally wrap everything try-catch and will let you handle the exception.如果您希望以类似的方式在 Web 上下文之外(以某种方式)处理您的异常,您将必须编写自己的方面,从字面上包装所有 try-catch 并让您处理异常。

In my case my "CustomUserDetailsService" is called inside a filter:在我的情况下,我的“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);
...

I handled it by catching the exceptions there:我通过在那里捕获异常来处理它:

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;
        }

Ideally, handle all exceptions with the controller advice, but this way it works理想情况下,使用控制器建议处理所有异常,但这样工作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM