我从 Spring 中通过 HTTP 请求调用的命令处理程序抛出一个
org.springframework.web.server.ResponseStatusException
。通常我希望响应携带由异常定义的 HTTP 状态。
但是,Spring 返回状态 200,而 Axon 记录:
Command 'com.my.Command' resulted in com.my.CustomResponseStatusException
。
我在命令网关上调用
send
还是sendAndWait
都没有区别。控制器直接返回结果CompletableFuture
。
是否有我缺少的配置?
我抛出的异常是一个扩展的
ResponseStatusException
,它允许我提供自定义域错误代码与 HTTP 状态错误代码:
public abstract class DomainException extends ResponseStatusException {
public final String errorCode;
public DomainException(HttpStatus status, String errorCode, String message) {
super(status, message);
this.errorCode = errorCode;
}
public DomainException(HttpStatus status, String errorCode, String message, Throwable cause) {
super(status, message, cause);
this.errorCode = errorCode;
}
}
这些具体实现之一是:
public class MembershipAlreadyExistsException extends DomainException {
public MembershipAlreadyExistsException(Id<Member> memberId, Id<Club> clubId) {
super(HttpStatus.BAD_REQUEST, DomainError.MEMBERSHIP_ALREADY_EXISTS.name(), "There is already a membership of member with ID " + memberId + " and club with ID " + clubId);
}
}
即使我提供自定义
@ControllerAdvice
,也没有调用任何异常处理程序方法,因为异常似乎是在命令网关中捕获并记录的。
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class DefaultExceptionHandler extends ResponseEntityExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionHandler.class);
@ExceptionHandler(UndeclaredThrowableException.class)
protected ResponseEntity<?> handleUndeclaredException(UndeclaredThrowableException exception) {
logger.error("Undeclared Exception", exception);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(DomainException.class)
protected ResponseEntity<?> handleDomainException(DomainException exception) {
logger.error("Domain Exception", exception);
return new ResponseEntity<>(exception.errorCode, exception.getStatusCode());
}
@ExceptionHandler(JSR303ViolationException.class)
protected ResponseEntity<?> handleJSR303ViolationException(JSR303ViolationException exception) {
logger.debug("Request was violating one ore more constraints", exception);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
有趣的是,如果我这样做:
@RequestMapping("/invite")
public CompletableFuture<Void> enrollMember(@RequestBody InviteMemberByEmail inviteMemberByEmail) {
try {
logger.trace("Before sending command");
CompletableFuture<Void> result = commandGateway.sendAndWait(inviteMemberByEmail);
logger.trace("After sending command");
return result;
} catch (Exception e) {
logger.trace("Exception caught", e);
throw e;
}
}
输出为:
2024-03-23T09:22:29.073+01:00 TRACE 20808 --- [nio-8081-exec-2] a.z.c.i.http.MemberController : Before sending command
2024-03-23T09:22:29.930+01:00 WARN 20808 --- [nio-8081-exec-2] o.s.c.annotation.AnnotationTypeMapping : Support for convention-based annotation attribute overrides is deprecated and will be removed in Spring Framework 6.2. Please annotate the following attributes in @org.axonframework.modelling.command.AggregateIdentifier with appropriate @AliasFor declarations: [routingKey]
2024-03-23T09:22:29.949+01:00 WARN 20808 --- [nio-8081-exec-2] o.a.c.gateway.DefaultCommandGateway : Command '--redacted--.api.InviteMemberToClub' resulted in --redacted--.domain.exceptions.MembershipAlreadyExistsException(400 BAD_REQUEST "There is already a membership of member with ID f6628294-4021-70d1-89cd-0ee5e7e99c8f and club with ID 780f0cc1-8eaf-4968-bcbd-8f92fb678260")
2024-03-23T09:22:29.950+01:00 TRACE 20808 --- [nio-8081-exec-2] a.z.c.i.http.MemberController : After sending command
我已经确认它实际上是
SimpleCommandBus
的一个实例,但注意到某种形式的 FailureLoggingCallback
:
我一直在检查
SimpleCommandBus
和 DefaultCommandGateway
的实现,但是 DefaultCommandGateway
不会重新抛出异常的唯一情况是 CommandResultMessage
不异常。请注意,CommandResultMessage
是Axon框架创建的用于携带命令处理结果的对象。因此,如果抛出异常,它将被捕获在
CommandResultMessage
中。这样做后,它的状态将被设置为异常。因此,此时我只能得出结论,命令处理程序可能正在执行有关异常抛出的操作。或者,聚合正在其他地方捕获异常。但是,为了能够推断出这一点,我需要要求您:
使用命令处理程序和
将
TrackingEventProcessor
切换为
SubscribingEventProcessor
将导致使用相同的线程来处理事件。因此,解决此问题的唯一方法是事件处理程序抛出一个异常,该异常确实会导致 CommandResultMessage
变得异常。