EnsureSuccess StatusCode 的用法及其引发的 HttpRequestException 的处理

HttpResponseMessage.EnsureSuccessStatusCode()的使用模式是什么?它处理消息的 Content 并抛出 HttpRequestException,但是我看不出如何通过编程方式处理它与通用 Exception有什么不同。例如,它不包括 HttpStatusCode,这将是方便的。

有什么办法能从中获得更多信息吗?有人可以显示 EnsureSuccessStatusCode()和 HttpRequestException 的相关使用模式吗?

106423 次浏览

我不喜欢“确保成功状态码”,因为它不会返回任何有意义的内容。 这就是为什么我创建了自己的扩展:

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

可以在 给你中找到微软的 EnsureSuccess StatusCode 的源代码

基于 链接的同步版本:

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

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

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
}

当我不想在同一个方法上处理 Exception 时,我使用 EnsureSuccess StatusCode。

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 上引发的异常将在 DoSomething Async 上处理。

下面是我提出的解决方案。唯一的缺陷是,由于 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);
}
}

空气污染指数

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

因为 2020年2月24日HttpRequestException.StatusCode属性是由 EnsureSuccessStatusCode.IIUC 设置的,所以这个问题中建议的扩展不再需要了。