Web Api 2 - 返回 NotFound(); vs 使用全局异常处理程序抛出异常

Web Api 2 - Returning NotFound(); vs throwing an exception with global exception handler

我正在使用 .net Web Api 2 开发 API。我看过很多关于 Web Api 版本 1 的博文和问题,但是相比之下,使用版本 2 中所做更改的答案似乎很少。

在控制器中比较这两种处理方式 'errors' ItemsController

一个。使用从 System.Web.Http.Results

创建对象的方法
// GET api/user/userID/item/itemID
[Route("{itemID:int}", Name="GetItem")]
[ResponseType(typeof(ItemDTO))]
public IHttpActionResult Get(int userID, int itemID)
{
    if (userID < 0 || itemID < 0) return BadRequest("Provided user id or item id is not valid");
    ItemDTO item = _repository.GetItem(itemID);

    if (item == null) return NotFound();

    if (item.UserID != userID) return BadRequest("Item userID does not match route userID");

    return Ok<ItemDTO>(item);
}

乙。可以通过注册自定义全局异常处理程序捕获的抛出异常

// ex) in WebApiConfig.cs
// config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
public class GlobalExceptionHandler : ExceptionHandler
{
    public override void Handle(ExceptionHandlerContext context)
    {
        Exception exception = context.Exception;

        HttpException httpException = exception as HttpException;

        if (httpException != null)
        {
            context.Result = new SimpleErrorResult(context.Request, (HttpStatusCode)httpException.GetHttpCode(), httpException.Message);
            return;
        }

        if (exception is RootObjectNotFoundException)
        {
            context.Result = new SimpleErrorResult(context.Request, HttpStatusCode.NotFound, exception.Message);
            return;
        }

        if (exception is BadRouteParametersException || exception is RouteObjectPropertyMismatchException)
        {
            context.Result = new SimpleErrorResult(context.Request, HttpStatusCode.BadRequest, exception.Message);
            return;
        }

        if (exception is BusinessRuleViolationException)
        {
            context.Result = new SimpleErrorResult(context.Request, (HttpStatusCode)422, exception.Message);
            return;
        }

        context.Result = new SimpleErrorResult(context.Request, HttpStatusCode.InternalServerError, exception.Message);
    }
}

GET api/user/userID/item/itemID
[Route("{itemID:int}", Name="GetItem")]
[ResponseType(typeof(ItemDTO))]
public IHttpActionResult Get(int userID, int itemID)
{
    if (userID < 0 || itemID < 0)
        throw new BadRouteParametersException("Provided user or item ID is not valid");
    ItemDTO item = _repository.GetItem(itemID);

    if (item.UserID != userID)
        throw new RouteObjectPropertyMismatchException("Item userID does not match route userID");

    return Ok<ItemDTO>(item);
}

这两个似乎都是有效的选择。由于我能够 return System.Web.Http.Results 对象,因此解决方案 A. 似乎是最好的。

但是考虑一下在我的 _repository 中,我的 GetItem 方法是这样实现的

public ItemDTO GetItem(int itemId)
{
    ItemInfo itemInfo = ItemInfoProvider.GetItemInfo(itemId);

    if (itemInfo == null) throw new RootObjectNotFoundException("Item not found");

    ItemDTO item = _autoMapper.Map<ItemDTO>(itemInfo);
    return item;
}

在这里,我可以跳过在 GetItem 中对 null 调用 autoMapper,也可以跳过在控制器中检查 null。

问题

我意识到我的问题更具架构性,而不是 'how do i use this feature' 但是同样,我没有找到太多关于如何以及何时使用这些不同功能的解释。

从我的角度来看,全局异常处理程序使单元测试每个操作更容易(阅读:更清晰)。您现在正在检查特定的 [expected] 异常与(本质上)比较状态代码。 (404 vs. 500 vs. 等)它还使 changes/logging 的错误通知(在 global/unified 级别)变得更加容易,因为你有一个单一的责任单位。

比如,你更喜欢写哪个单元测试?

[Test]
public void Id_must_not_be_less_than_zero()
{
    var fooController = new FooController();

    var actual = fooController.Get(-1);

    Assert.IsInstanceOfType(actual, typeof(BadRequestResult));
}

[Test]
[ExpectedException(typeof(BadRouteParametersException))]
public void Id_must_not_be_less_than_zero()
{
    var fooController = new FooController();

    var actual = fooController.Get(-1);
}

一般来说,我会说这更像是一种偏好,而不是一成不变的规则,你应该选择你认为最易于维护和最容易理解的任何东西,从入职的角度来看(项目上的新眼光)and/or 以后自己维护。

正如 Brad 所说,这在一定程度上取决于偏好。

使用 HTTP 代码与网络的工作方式一致,所以这是我的做法。

另一个考虑因素是抛出异常是有代价的。如果您愿意支付这笔费用,并在您的设计中考虑到这一点,那么做出这样的选择是可以的。请注意这一点,尤其是当您将异常用于并非真正异常的情况,而是您知道在正常应用程序流程中可能会遇到的事情时。

这是一个较旧的post,但这里有一个关于异常和性能主题的有趣讨论:

http://blogs.msdn.com/b/ricom/archive/2006/09/14/754661.aspx

及后续:

http://blogs.msdn.com/b/ricom/archive/2006/09/25/the-true-cost-of-net-exceptions-solution.aspx