在.NET客户端库中管理Web API的异常

时间:2015-04-08 12:31:43

标签: c# .net exception

我正在为公共Web API构建.NET客户端库。我努力解决的问题之一是如何最好地向库用户指出错误情况。显然,当执行失败时,我的库应该向调用者抛出或传递异常,但是我应该使用现有的异常还是为我的库创建新的异常?如果我创建新的异常,我应该只创建一个或多个异常吗?

选项1:仅使用现有的例外

最基本的,我可以举例如throw new Exception("API authentication error.");,但这是非常通用的,需要大量处理才能处理。我也可以使用更合适的现有异常,例如System.Security.Authentication.AuthenticationException,但是由于所有不同的命名空间,使用该库会很尴尬。在某些情况下,没有比InvalidOprationException更合适的情况,因此采用这种方法似乎会使使用该库来处理错误的人变得很麻烦。最简单的可能就是使用HttpException类,因为大多数错误条件在任何情况下都是HTTP状态代码和消息的转换。

选项2:为每种情况创建例外

我还可以为每个场景创建一个例外,但是将它们全部保存在库中。因此,我可以创建NotFoundExceptionAuthorizationExceptionTransactionsPendingException和其他特定例外。

选项3:为我的客户创建一个例外

第三个选项是创建一个自定义异常MyApiException,其中包含出现的不同错误的状态代码(当然还有适当的消息)。那么我可能会throw new MyApiException(401,"Not authorized.")

在我看来,选项3是最直接的实现,也是最容易处理的调用方法。我在GitHub和Codeplex上看到过使用这些选项的API客户端。是否有针对客户端库的推荐方法,或者它们都是有效的吗?

1 个答案:

答案 0 :(得分:2)

Microsoft在MSDN上提供了一些有关此内容的重要信息:

https://msdn.microsoft.com/en-us/library/seyhszts%28v=vs.110%29.aspx

就个人而言,我倾向于做以下事情:

  1. 在有意义时使用现有的例外

      

    e.g。传递无效密钥时,基于密钥的操作投掷KeyNotFoundException

  2. 为有保证的情况创建自定义例外

  3. 避免使用类似Either<TSuccess,TFail>等结果类型的例外情况
    1. 这方面的一个例子是:https://gist.github.com/siliconbrain/3923828
    2. 这对于像迭代器块这样的东西特别有用,在这些块中抛出异常可能会有问题,或者当你需要支持continue-on-error时
  4. 现有例外

    public int GetMyItem(string key)
    {
       if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key");
       if (!_myDictionary.ContainsKey(key))
       {
          throw new KeyNotFoundException();
       }
       // Additional implementation left out
    }
    

    自定义例外

    public class ArgumentNotOddException : ArgumentException
    {
        public ArgumentNotOddException(string paramName)
            : base(@"The argument must be odd", paramName)
        {
    
        }
    }
    
    public class MyClass
    {
        public void DoSomething(int x)
        {
            if (x%3 == 0) throw new ArgumentNotOddException("x");
        }
    }
    

    键入

    public class MyClass
    {
        public IEnumerable<IEither<int, string>> Divide(int left, IEnumerable<int> right)
        {
            foreach (var num in right)
            {
                if (num == 0)
                {
                    yield return Either.Right<int, string>("cannot divide by zero");
                }
                yield return Either.Left<int, string>(left/num);
            }
        }
    }
    

    你也可以创建一个使用异常代码和消息的MyAppException,但是你会失去表现力,异常代码容易出错输入(除非你使用枚举),{{1}并不能说MyAppException与<{1}}完全相同。

    我最好的建议是仔细思考代码应该抛出,或者它是否合理地返回混合结果/错误;在C#中这样做有点笨拙,但它消除了未处理异常的负担,并允许您在调用代码中更灵活。