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

时间:2015-07-01 20:28:14

标签: asp.net asp.net-web-api2

我正在开发使用.net Web Api 2开发的API。我已经看过很多关于Web Api版本1的博客帖子和SO问题,但使用版本2中所做更改的答案似乎比较稀缺。

比较这两种处理方式'错误'在控制器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);
}

B中。通过注册自定义全局异常处理程序

来抛出可以捕获的异常
// 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);
}

这两个似乎都是有效的选择。由于我能够返回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。

问题

  • 哪种方式更有意义?
  • 我应该尝试A&amp;组合B'
  • 我应该尝试让我的控制器保持精简,还是应该进行此类验证?因为我可以访问NotFound()和BadRequest()方法,所以处理逻辑会保留在那里吗?
  • 我应该在框架管道中的其他地方执行这种类型的逻辑吗?

我意识到我的问题更具体系结构,而不是“我如何使用此功能”#39;但是,我还没有找到太多关于如何以及何时使用这些不同功能的解释。

2 个答案:

答案 0 :(得分:4)

从我的观点来看,全局异常处理程序使单元测试每个操作更容易(阅读:更清晰)。您现在正在检查特定的[预期]异常与(基本上)比较状态代码。 (404与500 vs.等)它还使得更改/记录错误通知(在全球/统一级别)变得更容易,因为您只有一个责任单位。

例如,您更喜欢写哪个单元测试?

[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);
}

一般来说,我会说这更像是一种偏好,而不是一种强硬的规则,你应该从任何你认为最容易理解和最容易理解的内容(新眼睛)开始。从事项目工作)和/或以后自己维护。

答案 1 :(得分:2)

布拉德指出,这部分归结为偏好。

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

另一个考虑因素是抛出异常需要付出代价。如果您支付这笔费用,并在设计中考虑到这一点,那么做出这样的选择就没问题了。请注意这一点,特别是当您使用异常情况时,这些异常情况并非如此,而是您知道在正常应用程序流程中可能会遇到的情况。

这是一篇较旧的帖子,但这里有关于例外和表现主题的有趣讨论:

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