代码之家  ›  专栏  ›  技术社区  ›  Seb

使用CQRS模式返回API中有意义的错误

  •  3
  • Seb  · 技术社区  · 7 年前

    在使用Mediatr模式时,我发现向API控制器返回有意义的错误非常困难。让我们坐飞机吧 OrdersController.CancelOrder 方法为例( src ).

    在本例中,它们“仅”返回 Ok() BadRequest() .在这种情况下,他们将如何返回错误,如“此订单ID不存在”(404)或“此订单已发货”(400)(…)。

    我们可以引入一个新的类,叫做 Result 同时保存返回值(如果有)和潜在的错误消息。在这种情况下,所有命令、查询都应该返回 Result<YourModel> .我们也可以直接在控制器中添加代码。我拿不定主意这两种解决方案各有利弊。

    你怎么看?

    谢谢 塞布

    1 回复  |  直到 7 年前
        1
  •  1
  •   Alex    7 年前

    这正是我使用Mediatr的方式。
    返回一个包装类。

    以eShopOnContainers为例 CancelOrder 例如,我有一个命令,返回一个 CancelOrderCommandResult

    public class CancelOrderCommand : IRequest<CancelOrderCommandResult>
    { }
    

    这个 取消订单命令结果 可能是这样的:

    public class CancelOrderCommandResult
    {
        public CancelOrderCommandResult(IEnumerable<Error> errors)
        {
            Success = false;
            Errors = errors;
        }
    
        public CancelOrderCommandResult(bool success)
        {
            Success = success;
        }
    
        public bool Success {get; set;}
    
        public IEnumerable<Error> Errors {get; set;}
    }
    

    我省略了 Error 类,但它可能只是包含错误信息、错误代码等的POCO。。。

    然后我们的处理者就变成了

    public class CancelOrderCommandHandler : IRequestHandler<CancelOrderCommand, CancelOrderCommandResult>
    {
        private readonly IOrderRepository _orderRepository;
    
        public CancelOrderCommandHandler(IOrderRepository orderRepository)
        {
            _orderRepository = orderRepository;
        }
    
        public async Task<bool> Handle(CancelOrderCommand command, CancellationToken cancellationToken)
        {
            var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
    
            if(orderToUpdate == null)
            {
                return false;
            }
    
            try 
            {
                orderToUpdate.SetCancelledStatus();
                await _orderRepository.UnitOfWork.SaveEntitiesAsync();
    
                //iff success, return true
                return new CancelOrderCommandResult(true);
            }
            catch (Exception ex)
            {
                var errors = MapErrorsFromException(ex);
                return new CancelOrderCommandResult(errors)
            }
        }
    }
    

    再一次 MapErrorsFromException 为简洁起见,省略了,但您甚至可以将其作为依赖项注入。

    在你的控制器中,当你呼叫 _mediator.Send 你现在可以回去了 取消订单命令结果 -如果 .Success 如果是真的,请像以前一样返回200。

    否则,你会有一个错误集合——你可以用它来决定返回什么——400、500等等。。。