正确使用EnsureSuccessStatusCode和IsSuccessStatusCode

Des*_*xAZ 3 c# exception-handling httpresponse asp.net-web-api

我正在调用我的Web API HttpClient,我看到有一个EnsureSuccessStatusCode方法和一个IsSuccessStatusCode属性.哪一个合适?

我读了这篇文章,还有一些问题:

使用EnsureSuccessStatusCode并处理它抛出的HttpRequestException

我遇到的问题是,如果我发送一个GET请求,我传递了ID我想要检索的对象,基本上有两个结果:

  1. 我以状态200返回对象
  2. 我可以返回null,因为没有匹配,但是这会导致状态为404.

如果我调用EnsureSuccessStatusCode()状态404将导致抛出异常.这并不理想,因为当我测试我的代码时,我一直收到404错误,这首先让我觉得API URL不正确,但实际上没有匹配的对象与提供的Id.在这种情况下,我宁愿返回一个null对象而不是抛出异常.

所以我尝试检查IsSuccessfulStatusCode属性的值.这似乎是一个更好的选择,当此属性为false时,我可以返回一个null对象,但是,有许多状态代码可能导致此属性具有false值.404是其中之一,但还有其他几个状态代码,例如400 Bad Request,405 Method Not Allowed等.我想记录除404之外的所有不成功的错误代码的异常,我想知道是否有一种更好的方法可以做到这一点,而不是检查ResponseCode响应的值,然后抛出一个由我的catch块捕获的异常,这是记录发生的地方.

这是我的GET方法的代码:

public static Customer GetCustomerByID(int id)
{
    try
        {
            using (var client = GetConfiguredClient())
            {
                Customer customer = null;
                var requestUri = $"Customers/{id}";

                using (var response = client.GetAsync(requestUri).Result)
                {
                    if (response.IsSuccessStatusCode)
                        customer = response.Content.ReadAsAsync<Customer>().Result;
                }

                return customer;
            }
        }
        catch (Exception ex)
        {
          ex.Data.Add(nameof(id), id);

          LogException(ex);

          throw;
        }
    }
Run Code Online (Sandbox Code Playgroud)

Customer如果返回非成功的状态代码并且没有记录任何内容,则此代码将返回null .

处理这种情况的最佳方法是什么?

Flo*_*ter 10

接受的答案采用“异常分支”,这被一些人认为是反模式。以下是如何使用EnsureSuccessStatusCode和 ,IsSuccessStatusCode仅针对意外或不能或不应“本地”处理的错误使用异常:

  1. 如果您想处理特定的错误响应,请直接使用if 语句处理它们。
  2. 如果您想将所有(剩余的)错误响应视为意外错误,请使用EnsureSuccessStatusCode并且不捕获异常,但假设它将由实际上可以执行某些操作的 catch 处理程序处理(例如更高级别的应用程序逻辑,或通用顶级错误处理程序)。
  3. 如果您想对所有(剩余)错误响应(例如日志记录)执行某些操作,但随后正常继续,或者如果您想抛出自己类型的异常,请使用IsSuccessStatusCode.

这种方法为您提供了异常的所有优点,同时最大限度地减少了缺点(例如,在您可能不感兴趣的完全正常的事件上中断调试器,或者用 catch 块填充代码,这些块比 if 更难读写-声明)。

例子:

using (var response = client.GetAsync(requestUri).Result)
{
  if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
  {
    // TODO: Special handling for "401 Unauthorized" goes here
  }
  else
  {
    // All other unsuccessful error codes throw
    response.EnsureSuccessStatusCode();

    // TODO: Handling of successful response goes here
  }
}
Run Code Online (Sandbox Code Playgroud)

...或者如果您想读取错误响应或进行日志记录等:

using (var response = client.GetAsync(requestUri).Result)
{
  if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
  {
    // TODO: Special handling for "401 Unauthorized" goes here
  }
  else if (!response.IsSuccessStatusCode)
  {
    // TODO: Read error response, logging, throw custom exception, etc., goes here

    // TODO: Keep this if you still want to throw the standard exception.
    // TODO: Otherwise, remove this.
    response.EnsureSuccessStatusCode();
  }
  else
  {
    // TODO: Handling of successful response goes here
  }
}
Run Code Online (Sandbox Code Playgroud)


Cod*_*und 6

因为这:

所以我尝试检查IsSuccessfulStatusCode 属性的值.这似乎是一个更好的选择,当此属性为false时,我可以返回一个null对象,但是,有许多状态代码可能导致此属性具有false值.404是其中之一,但还有其他几个状态代码,例如400 Bad Request,405 Method Not Allowed等.我想记录除404之外的所有不成功的错误代码的异常,我想知道是否有一种更好的方法可以做到这一点,而不是检查ResponseCode 响应的值,然后抛出一个由我的catch块捕获的异常,这是记录发生的地方.

我会使用该EnsureSuccessStatusCode方法,然后修改catch块,如下所示:

public static Customer GetCustomerByID(int id)
{
    try
    {
        using (var client = GetConfiguredClient())
        {
            var requestUri = $"Customers/{id}";
            Customer customer;

            using (var response = client.GetAsync(requestUri).Result)
            {
                try 
                {
                    response.EnsureSuccessStatusCode();
                    // If we reach here it means we can get the customer data.
                    customer = response.Content.ReadAsAsync<Customer>().Result;
                }
                catch(HttpRequestException)
                {
                    if(response.StatusCode == HttpStatusCode.NotFound) // 404
                    {
                        customer = null;
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            return customer;
        }
    }
    catch (Exception ex)
    {
        ex.Data.Add(nameof(id), id);

        LogException(ex);

        throw;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这种模式被称为异常分支,被一些人认为是不好的做法。 (4认同)
  • 我不喜欢“EnsureSuccessStatusCode”,因为它会处理“response.Content”对象。错误响应可能包含丰富的错误详细信息。如果您使用“EnsureSuccessStatusCode”,您将失去所有这些。剩下的就是异常块中的“StatusCode”,它只能说明部分情况。这是我的2c。 (4认同)
  • 我认为推断添加when 子句并不意味着您的代码现在因异常而分支。当您以抛出异常然后将它们吞入 catch 语句的方式构建代码时,您就是按异常分支。像积极的 if 语句一样有效地使用异常状态。这不是 try/catch 语句“应该”使用的用途。When 子句只是增加了您想要捕获的异常的粒度。 (2认同)
  • +1 @onefootswill - 我今天遇到了一个问题,开发人员在阅读“response.Content”之前调用了“response.EnsureSuccessStatusCode();”。就我而言,这隐藏了 Autofac 缺少类型注册的问题。我认为更大的问题是,当请求没有成功的状态代码时,尝试读取“response.Content”是否不安全或者是一个坏主意。 (2认同)
  • 如果您总是要捕获异常,请考虑不要抛出它。如果您启用“异常中断”来查找实际上是错误的异常的原因,则在调试时按异常分支可能会很烦人。 (2认同)