我正在开发使用.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);
}
// 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。
我意识到我的问题更具体系结构,而不是“我如何使用此功能”#39;但是,我还没有找到太多关于如何以及何时使用这些不同功能的解释。
答案 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