使用EnsureSuccessStatusCode并处理它抛出的HttpRequestException

G. *_*nev 90 .net system.net httprequest

什么是使用模式HttpResponseMessage.EnsureSuccessStatusCode()?它处理消息的内容并抛出HttpRequestException,但是我没有看到如何以编程方式处理它而不是通用的Exception.例如,它不包括HttpStatusCode,这将是方便的.

有没有办法从中获取更多信息?任何人都可以显示两者的相关使用模式EnsureSuccessStatusCode()和HttpRequestException吗?

Tim*_*lds 136

EnsureSuccessStatusCode当您不想以任何特定方式处理故障情况时,惯用法是简明地验证请求是否成功.当您想要快速建立客户端原型时,这尤其有用.

当您决定以特定方式处理故障情况时,请不要执行以下操作.

var response = await client.GetAsync(...);
try
{
    response.EnsureSuccessStatusCode();
    // Handle success
}
catch (HttpRequestException)
{
    // Handle failure
}
Run Code Online (Sandbox Code Playgroud)

这引发了一个异常只是为了立即捕获它,这没有任何意义.为此目的的IsSuccessStatusCode财产HttpResponseMessage.请执行以下操作.

var response = await client.GetAsync(...);
if (response.IsSuccessStatusCode)
{
    // Handle success
}
else
{
    // Handle failure
}
Run Code Online (Sandbox Code Playgroud)

  • @NickG`(int)response.StatusCode`(参见https://msdn.microsoft.com/en-us/library/system.net.http.httpresponsemessage.statuscode(v=vs.118).aspx) (10认同)

paj*_*ics 82

我不喜欢EnsureSuccessStatusCode,因为它不会返回任何有意义的东西.这就是为什么我创建了自己的扩展:

public static class HttpResponseMessageExtensions
{
    public static async Task EnsureSuccessStatusCodeAsync(this HttpResponseMessage response)
    {
        if (response.IsSuccessStatusCode)
        {
            return;
        }

        var content = await response.Content.ReadAsStringAsync();

        if (response.Content != null)
            response.Content.Dispose();

        throw new SimpleHttpResponseException(response.StatusCode, content);
    }
}

public class SimpleHttpResponseException : Exception
{
    public HttpStatusCode StatusCode { get; private set; }

    public SimpleHttpResponseException(HttpStatusCode statusCode, string content) : base(content)
    {
        StatusCode = statusCode;
    }
}
Run Code Online (Sandbox Code Playgroud)

可以在此处找到Microsoft的EnsureSuccessStatusCode的源代码

基于SO链接的同步版本:

public static void EnsureSuccessStatusCode(this HttpResponseMessage response)
{
    if (response.IsSuccessStatusCode)
    {
        return;
    }

    var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();

    if (response.Content != null)
        response.Content.Dispose();

    throw new SimpleHttpResponseException(response.StatusCode, content);
}
Run Code Online (Sandbox Code Playgroud)

我不喜欢IsSuccessStatusCode,它不是"很好"可重用的.例如,您可以使用像polly这样的库来重复网络问题时的请求.在这种情况下,您需要您的代码引发异常,以便polly或其他一些库可以处理它...

  • 我不明白为什么你先`等待response.Content.ReadAsStringAsync()`然后检查`if(response.Content!= null)` (3认同)
  • Polly现在处理返回结果和异常,正是为了帮助这种情况.您可以配置Polly以保护`HttpRequest`调用,并将策略配置为处理某些异常,以及某些`HttpResponseCode`.请参阅[Polly自述文件中的示例](https://github.com/App-vNext/Polly#handing-return-values-and-policytresult) (3认同)
  • 您的版本与`EnsureSuccessStatusCode`的原始实现不同.你总是处理`response.Content`(因为最终甚至在`return;`语句之后被调用)并且它会破坏内容以便进一步阅读.原始实现仅在状态代码未指示成功结果时才处理内容. (2认同)
  • 当它刚刚调用了一个方法时,`response.Content`怎么能为null? (2认同)

Joh*_*ski 8

对于 .NET 5.0 或更高版本,HttpRequestException 返回 HttpStatusCode 属性,您应该改用该属性。

以下是我针对 .NET 5.0 之前版本提出的解决方案。唯一的缺陷是,由于 ASP.NET Core 框架资源管理器位于框架内部,因此我无法直接重用 Microsoft 的国际化消息字符串,因此我在这里仅使用逐字英文消息文字。

优点

  • 记录 5xx 服务器错误的内容
    • 有时,服务器错误实际上是客户端错误的变相,例如客户端使用了最终被关闭的已弃用端点。
  • 使用以下命令编写集成测试时可以更轻松地发现错误ConfigureTestContainer<T>

缺点

  • 效率低下。
    • 如果你阅读响应内容,并且内容很长,你会减慢客户端的速度。对于一些具有软实时响应要求的客户来说,这种抖动可能是不可接受的。
  • 对错误记录和监控的责任不正确。
    • 如果这是 5xx 服务器错误,既然客户端没有做错什么,为什么客户端要关心呢?打电话response.EnsureSuccessStatusCode();让服务员处理就可以了。
    • 当出现内部服务器错误时,为什么不直接检查服务器错误日志呢?
  • Content需要在检查状态之前读取属性。在某些情况下,这可能是不可取的,其中之一就是效率低下。

用法

using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, "controller/action"))
{
  using (var response = await HttpClient.SendAsync(requestMessage))
  {
    var content = await response.Content.ReadAsStringAsync();
    response.EnsureSuccessStatusCode2(content);
    var result = JsonConvert.DeserializeObject<ResponseClass>(content);
  }
}
Run Code Online (Sandbox Code Playgroud)

应用程序编程接口

    public static class HttpResponseMessageExtensions
    {
        public static void EnsureSuccessStatusCode2(this HttpResponseMessage message, string content = null)
        {
            if (message.IsSuccessStatusCode)
                return;
            var contentMessage = string.IsNullOrWhiteSpace(content) ? string.Empty : $"Content: {content}";
            throw new HttpRequestException(string.Format(
                System.Globalization.CultureInfo.InvariantCulture,
                "Response status code does not indicate success: {0} ({1}).{2}",
                (int)message.StatusCode,
                message.ReasonPhrase,
                contentMessage)
                );
        }
    }
Run Code Online (Sandbox Code Playgroud)


Sér*_*eno 6

当我不想处理同一方法上的异常时,我使用 EnsureSuccessStatusCode 。

public async Task DoSomethingAsync(User user)
{
    try
    {
        ...
        var userId = await GetUserIdAsync(user)
        ...
    }
    catch(Exception e)
    {
        throw;
    }
}

public async Task GetUserIdAsync(User user)
{
    using(var client = new HttpClient())
    {
        ...
        response = await client.PostAsync(_url, context);

        response.EnsureSuccesStatusCode();
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

GetUserIdAsync 上引发的异常将在 DoSomethingAsync 上处理。