EnsureSuccessStatusCode 的使用和它抛出的 HttpRequestException 的处理

Posted

技术标签:

【中文标题】EnsureSuccessStatusCode 的使用和它抛出的 HttpRequestException 的处理【英文标题】:Usage of EnsureSuccessStatusCode and handling of HttpRequestException it throws 【发布时间】:2014-02-01 13:53:51 【问题描述】:

HttpResponseMessage.EnsureSuccessStatusCode()的使用模式是什么?它处理消息的内容并抛出HttpRequestException,但我看不出如何以与通用Exception 不同的方式以编程方式处理它。例如,它不包括本来很方便的HttpStatusCode

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

【问题讨论】:

【参考方案1】:

EnsureSuccessStatusCode 的惯用用法是在您不想以任何特定方式处理失败情况时简明地验证请求是否成功。当您想要快速制作客户端原型时,这尤其有用。

当您决定要以特定方式处理失败案例时,不要执行以下操作。

var response = await client.GetAsync(...);
try

    response.EnsureSuccessStatusCode();
    // Handle success

catch (HttpRequestException)

    // Handle failure

这抛出一个异常只是为了立即捕获它,这没有任何意义。 HttpResponseMessageIsSuccessStatusCode 属性就是为此目的而存在的。请改为执行以下操作。

var response = await client.GetAsync(...);
if (response.IsSuccessStatusCode)

    // Handle success

else

    // Handle failure

【讨论】:

有没有办法得到真正的integer状态码?当我尝试这个时,我得到一个诸如“NotFound”之类的字符串,而不是 404 状态码。 @NickG (int)response.StatusCode(见msdn.microsoft.com/en-us/library/…) 注意, EnsureSuccessStatusCode() 抛出的默认 HttpRequestException 将带有原因短语。但是,如果它不成功,您仍然可以在响应中访问该属性。 @TimothyShields 我很欣赏你在允许快速原型制作方面的态度。为什么不先阅读response.Content 值,然后再跳入// Handle success// Handle failure 块?这样,您只需读取一次response.Content 属性。我可以看到这样做的唯一缺点是如果 Content 属性是一个长字符串,那么您基本上会减慢客户端的速度,但是如果您担心速度,为什么不直接使用response.EnsureSuccessStatusCode();?跨度> 我在下面编写了自己的 EnsureSuccessStatusCode 版本。 ***.com/a/63476616/1040437 它委托调用者负责在检查状态之前获取Content【参考方案2】:

我不喜欢 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;
    

可以在here找到微软 EnsureSuccessStatusCode 的源代码

基于SO link的同步版本:

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

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

【讨论】:

同意,默认代码缺少从返回中获取有意义消息的功能。 您的版本与EnsureSuccessStatusCode 的原始实现不同。您总是处置 response.Content (因为即使在 return; 语句之后总是调用 finally )并且它会破坏内容以供进一步阅读。原始实现仅在状态码不表示成功结果时才处理内容。 我不明白你为什么先await response.Content.ReadAsStringAsync()然后检查if (response.Content != null) Polly 现在处理返回结果和异常,正是为了帮助处理这种情况。您可以配置 Polly 以保护 HttpRequest 呼叫,并配置策略以处理某些异常和某些 HttpResponseCodes。见example in the Polly readme here response.Content 刚刚调用了一个方法,它怎么可能为 null?【参考方案3】:

当我不想在同一方法上处理异常时,我使用 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();
        ...
    

GetUserIdAsync 上抛出的异常将在 DoSomethingAsync 上处理。

【讨论】:

【参考方案4】:

以下是我提出的解决方案。唯一的缺陷是,由于 ASP.NET Core 框架资源管理器是框架内部的,我不能直接重用微软的国际化消息字符串,所以我这里只使用逐字英文消息文字。

优点

记录 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);
  

API

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

【讨论】:

以上是关于EnsureSuccessStatusCode 的使用和它抛出的 HttpRequestException 的处理的主要内容,如果未能解决你的问题,请参考以下文章

UWP-HttpClient

UWP-HttpClient

关于光的反射和折射的例子?

既是3的倍数又是5的倍数都有哪些

一个三位数既是3的倍数,又是5的倍数。这样的三位数最小是啥

数组的创建,及数组的方法