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

6 asp.net mediator .net-core mediatr asp.net-core

当使用 Mediatr 模式时,我发现向 API 控制器返回有意义的错误非常具有挑战性。我们以该OrdersController.CancelOrder方法为例(src)。

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

我们可以引入一个新类,称为Result保存返回值(如果有)和潜在的错误消息。在这种情况下,您的所有命令、查询都应返回Result<YourModel>. 我们还可以直接在控制器内添加代码。我无法确定这两种解决方案都有优点和缺点。

你对此有何看法?

谢谢塞布

Ale*_*lex 4

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

如果我们以 eShopOnContainers 为例CancelOrder,我会得到命令,返回一个CancelOrderCommandResult

public class CancelOrderCommand : IRequest<CancelOrderCommandResult>
{ }
Run Code Online (Sandbox Code Playgroud)

可能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;}
}
Run Code Online (Sandbox Code Playgroud)

我省略了该类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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

在你的控制器中,当你调用时,_mediator.Send你现在会返回CancelOrderCommandResult- 如果.Success是 true,则像以前一样返回 200。

否则,您将得到一系列错误 - 您可以通过这些错误来决定返回什么 - 400、500 等......

  • 是的,那是一个好方法。另一种方法是在处理程序中抛出异常,但这有点违背“不要使用异常进行流程控制”。 (3认同)