【问题标题】:Global exception handling in micronaut Javamicronaut Java 中的全局异常处理
【发布时间】:2021-01-16 07:46:23
【问题描述】:

正在寻找更好的解决方案来处理 micronaut 中的全局异常https://docs.micronaut.io/latest/guide/index.html#errorHandling

控制器

@Controller("/category")
public class CategoryController {
@Delete(uri = "/{id}")
public Maybe<HttpResponse> delete(@NotBlank String id) {
            LOG.info(String.format("API --> Deleting the specified category"));
            return iCategoryManager.Count(id).flatMap(item -> {
                if (item > 0) {
                    iCategoryManager.Delete(id).subscribe();
                    return Maybe.just(HttpResponse.noContent());
                } else
                    return Maybe.just(HttpResponse.notFound());
            });
    }
}

iCategoryManager.Count(id) 导致如下异常,如何在 GlobalExceptionHandler

上捕获异常
io.micronaut.core.serialize.exceptions.SerializationException: Incorrect message body size to deserialize to a Long
    at io.micronaut.rabbitmq.serdes.JavaLangRabbitMessageSerDes$LongSerDes.deserialize(JavaLangRabbitMessageSerDes.java:314)
    at io.micronaut.rabbitmq.serdes.JavaLangRabbitMessageSerDes$LongSerDes.deserialize(JavaLangRabbitMessageSerDes.java:306)
    at io.micronaut.rabbitmq.serdes.JavaLangRabbitMessageSerDes.deserialize(JavaLangRabbitMessageSerDes.java:81)
    at io.micronaut.rabbitmq.intercept.RabbitMQIntroductionAdvice.deserialize(RabbitMQIntroductionAdvice.java:323)
    at io.micronaut.rabbitmq.intercept.RabbitMQIntroductionAdvice.lambda$intercept$22(RabbitMQIntroductionAdvice.java:268)
    at io.reactivex.internal.operators.flowable.FlowableFlatMap$MergeSubscriber.onNext(FlowableFlatMap.java:132)
    at io.micronaut.reactive.rxjava2.RxInstrumentedSubscriber.onNext(RxInstrumentedSubscriber.java:59)
    at io.reactivex.internal.operators.flowable.FlowableTimeoutTimed$TimeoutSubscriber.onNext(FlowableTimeoutTimed.java:101)
    at io.micronaut.reactive.rxjava2.RxInstrumentedSubscriber.onNext(RxInstrumentedSubscriber.java:59)
    at io.reactivex.internal.subscriptions.DeferredScalarSubscription.complete(DeferredScalarSubscription.java:132)
    at io.reactivex.internal.operators.single.SingleToFlowable$SingleToFlowableObserver.onSuccess(SingleToFlowable.java:62)
    at io.micronaut.reactive.rxjava2.RxInstrumentedSingleObserver.onSuccess(RxInstrumentedSingleObserver.java:65)
    at io.reactivex.internal.operators.single.SingleFlatMap$SingleFlatMapCallback$FlatMapSingleObserver.onSuccess(SingleFlatMap.java:111)
    at io.micronaut.reactive.rxjava2.RxInstrumentedSingleObserver.onSuccess(RxInstrumentedSingleObserver.java:65)
    at io.reactivex.internal.operators.single.SingleDoFinally$DoFinallyObserver.onSuccess(SingleDoFinally.java:73)
    at io.micronaut.reactive.rxjava2.RxInstrumentedSingleObserver.onSuccess(RxInstrumentedSingleObserver.java:65)
    at io.reactivex.internal.operators.single.SingleCreate$Emitter.onSuccess(SingleCreate.java:67)
    at io.micronaut.rabbitmq.reactive.RxJavaReactivePublisher$3.handleDelivery(RxJavaReactivePublisher.java:324)
    at com.rabbitmq.client.impl.ConsumerDispatcher$5.run(ConsumerDispatcher.java:149)
    at com.rabbitmq.client.impl.ConsumerWorkService$WorkPoolRunnable.run(ConsumerWorkService.java:104)

全局异常处理

@Produces
@Singleton
@Requires(classes = {GlobalException.class, ExceptionHandler.class})
public class GlobalExceptionHandler implements ExceptionHandler<GlobalException, HttpResponse> {

    @Override
    public HttpResponse handle(HttpRequest request, GlobalException exception) {
        return HttpResponse.ok(0);
    }
}

public class GlobalException extends RuntimeException{
}

如何挂钩 GlobalExceptionHandler。应用程序上发生的任何异常都应在 GlobalExceptionHandler

处捕获

【问题讨论】:

  • 为什么不专门为io.micronaut.core.serialize.exceptions.SerializationException创建一个异常处理程序
  • 我尝试为Exception 创建一个处理程序,但没有成功,所以我改为使用class ConversionErrorHandler : ExceptionHandler&lt;ConversionErrorException, HttpResponse&lt;*&gt;,它运行良好
  • 另外,您应该尝试使用 import io.micronaut.context.annotation.Primary 注释您的类,以便它有利于您的异常处理程序而不是其他处理程序。
  • @Archmede 你能发表答案吗
  • 在这里查看我的答案stackoverflow.com/questions/56676486/…

标签: java micronaut micronaut-client micronaut-rest


【解决方案1】:

当服务器上发生异常时,会在与抛出的异常最匹配的 bean 上下文中查找ExceptionHandler(通过处理程序的类型参数)。要处理所有异常,需要替换所有提供的异常处理程序。大约有十几个。

一种选择是替换默认的错误响应处理器。例如,这就是身体成为 HATEOS 的原因。有关详细信息,请参阅有关错误处理的 documentation

【讨论】:

  • 好吧,上面的代码在运行时并没有捕捉到任何异常,但是如果我手动抛出异常,它已经被捕捉到了。我有一个抛出异常的兔子生产者,但这些异常没有被捕获
【解决方案2】:

这个解决方案适合我

@Produces
@Singleton
@Requires(classes = {GlobalException.class, ExceptionHandler.class})
public class GlobalExceptionHandler implements ExceptionHandler<GlobalException, HttpResponse> {
    private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @Override
    public HttpResponse handle(HttpRequest request, GlobalException exception) {
        LOG.error(exception.getLocalizedMessage());
        LOG.error(exception.getCause().getMessage());
        Arrays.stream(exception.getStackTrace()).forEach(item ->  LOG.error(item.toString()));
        return HttpResponse.serverError(exception.getLocalizedMessage());
    }
}

public class GlobalException extends RuntimeException{
    public GlobalException(Throwable throwable){super(throwable);}
}

控制器

  public Maybe<FindProductCommand> get(ProductSearchCriteriaCommand searchCriteria) {
        LOG.info("Controller --> Finding all the products");
        return iProductManager.find(searchCriteria)
                .onErrorResumeNext(throwable ->  { return Maybe.error(new GlobalException(throwable));});
    }

使用 Micronaut 3 和 project reactor 进行更新

@Override
@SingleResult
public Flux<List<FindProductCommand>> freeTextSearch(String text) {
    LOG.info("Controller --> Finding all the products");
    return iProductManager.findFreeText(text)
            .onErrorMap(throwable -> {
                throw new GlobalException(throwable);
            });
}

【讨论】:

    【解决方案3】:

    我们可以使用import io.micronaut.http.server.exceptions.ExceptionHandler 用于全局错误处理。

    创建错误消息:

    public class ErrorMessage {
    
        private String message;
        private Boolean status;
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public Boolean getStatus() {
            return status;
        }
    
        public void setStatus(Boolean status) {
            this.status = status;
        }
    
    }
    

    创建自定义例外:

    import java.io.Serializable;
    
    public class CustomException extends RuntimeException 
        implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        public CustomException() {
        }
    
        public CustomException(String message) {
            super(message);
        }
    
        public CustomException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public CustomException(Throwable cause) {
            super(cause);
        }
    
        public CustomException(String message, Throwable cause, 
        boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    

    创建自定义异常处理程序:

    import io.micronaut.context.annotation.Requires;
    import io.micronaut.http.HttpRequest;
    import io.micronaut.http.HttpResponse;
    import io.micronaut.http.HttpStatus;
    import io.micronaut.http.annotation.Produces;
    import io.micronaut.http.server.exceptions.ExceptionHandler;
    import jakarta.inject.Singleton;
    
    @Produces
    @Singleton
    @Requires(classes = { CustomException.class, ExceptionHandler.class })
    
    public class CustomExceptionHandler 
        implements ExceptionHandler<CustomException, HttpResponse<ErrorMessage>> {
    
        @Override
        public HttpResponse<ErrorMessage> 
            handle(HttpRequest request, CustomException exception) {
    
            ErrorMessage message = new ErrorMessage();
            message.setMessage(exception.getMessage());
            message.setStatus(false);
            return HttpResponse.serverError(message).
                    status(HttpStatus.BAD_REQUEST);
        }
    
    }
    

    演示控制器:

    import com.knf.dev.demo.data.UserData;
    import com.knf.dev.demo.exception.CustomException;
    import com.knf.dev.demo.model.User;
    import io.micronaut.http.annotation.Controller;
    import io.micronaut.http.annotation.Get;
    
    @Controller
    public class UserController {
    
        protected final UserData userData;
    
        public UserController(UserData userData) {
            this.userData = userData;
        }
    
        @Get("/users/{id}")
        public User findUserById(String id) throws CustomException {
            Long user_id = null;
            try {
                user_id = Long.parseLong(id);
            } catch (NumberFormatException e) {
                throw new CustomException("User Id must be numeric");
            }
            User user = userData.getUserById(user_id);
            if (user == null) {
                throw new CustomException("Entity Not Found");
            }
            return user;
        }
    }
    

    验证 REST API

    案例 1:无效请求(未找到实体):

    案例 2:无效请求(用户 ID 必须为数字):

    案例 3:有效请求:

    【讨论】:

      猜你喜欢
      • 2021-06-07
      • 2010-12-05
      • 1970-01-01
      • 1970-01-01
      • 2016-11-03
      • 2021-08-19
      • 2019-07-18
      • 2017-02-24
      • 2011-05-20
      相关资源
      最近更新 更多