Return 使用 CQRS 模式 API 中有意义的错误
Return meaningful errors in API using CQRS pattern
当使用 Mediatr 模式时,我发现 return 对 API 控制器有意义的错误非常具有挑战性。我们以OrdersController.CancelOrder
方法为例(src).
在这个例子中,他们 "only" return Ok()
和 BadRequest()
。在那种情况下,他们将如何 return 错误,例如 "This orderid does not exist" (404) 或 "this order has been shipped" (400) (...)。
我们可以引入一个名为 Result
的新 class,其中包含 returned 值(如果有)和潜在的错误消息。在那种情况下,您所有的命令、查询都应该 return Result<YourModel>
。我们也可以直接在控制器中添加代码。我无法确定这两种解决方案各有利弊。
你怎么看?
谢谢
塞布
这正是我倾向于使用 Mediatr 的方式。
Return 包装器 class.
如果我们以 eShopOnContainers 示例 CancelOrder
为例,我将使用命令 return a CancelOrderCommandResult
public class CancelOrderCommand : IRequest<CancelOrderCommandResult>
{ }
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
class,但它可能只是包含错误信息、错误代码等的 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
时,您现在会返回 CancelOrderCommandResult
- 如果 .Success
为真,return 和以前一样返回 200。
否则,您会遇到一系列错误 - 您可以根据这些错误做出一些决定 return - 400、500 等...
当使用 Mediatr 模式时,我发现 return 对 API 控制器有意义的错误非常具有挑战性。我们以OrdersController.CancelOrder
方法为例(src).
在这个例子中,他们 "only" return Ok()
和 BadRequest()
。在那种情况下,他们将如何 return 错误,例如 "This orderid does not exist" (404) 或 "this order has been shipped" (400) (...)。
我们可以引入一个名为 Result
的新 class,其中包含 returned 值(如果有)和潜在的错误消息。在那种情况下,您所有的命令、查询都应该 return Result<YourModel>
。我们也可以直接在控制器中添加代码。我无法确定这两种解决方案各有利弊。
你怎么看?
谢谢 塞布
这正是我倾向于使用 Mediatr 的方式。
Return 包装器 class.
如果我们以 eShopOnContainers 示例 CancelOrder
为例,我将使用命令 return a CancelOrderCommandResult
public class CancelOrderCommand : IRequest<CancelOrderCommandResult>
{ }
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
class,但它可能只是包含错误信息、错误代码等的 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
时,您现在会返回 CancelOrderCommandResult
- 如果 .Success
为真,return 和以前一样返回 200。
否则,您会遇到一系列错误 - 您可以根据这些错误做出一些决定 return - 400、500 等...