确保使用SuccessSuccessStatusCode并处理抛出的HttpRequestException


104

的使用方式是HttpResponseMessage.EnsureSuccessStatusCode()什么?它处理消息的内容并引发HttpRequestException,但是我没看到如何以编程方式处理消息而不是普通消息Exception。例如,它不包含HttpStatusCode会很方便的。

有什么方法可以获取更多信息吗?谁能显示EnsureSuccessStatusCode()HttpRequestException和HttpRequestException的相关使用模式?

Answers:


156

EnsureSuccessStatusCode当您不想以任何特定方式处理失败案例时,的惯用法是简洁地验证请求的成功。当您要快速建立客户端原型时,这特别有用。

当您决定要以特定方式处理故障案例时,请勿执行以下操作。

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

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

var response = await client.GetAsync(...);
if (response.IsSuccessStatusCode)
{
    // Handle success
}
else
{
    // Handle failure
}

1
有什么方法可以获取真实的整数状态码?当我尝试此操作时,我得到一个字符串,例如“ NotFound”,而不是404状态代码。
NickG '16

12
@NickG (int)response.StatusCode(见msdn.microsoft.com/en-us/library/...
蒂莫西·希尔兹

1
请注意,EnsureSuccessStatusCode()引发的默认HttpRequestException将具有原因短语。但是,如果响应失败,您仍然可以在响应中访问该属性。
Stefan Zvonar'7

@StefanZvonar除了您所写的内容外,我找不到异常原因短语。
KansaiRobot

1
@NickG您可以使用(int)response.StatusCode来获取HTTP状态代码的数字值
Henrik HolmgaardHøyer19年

95

我不喜欢SecureSuccessStatusCode,因为它不会返回任何有意义的信息。这就是为什么我创建自己的扩展程序的原因:

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

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

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

我不喜欢IsSuccessStatusCode的地方在于它不是“很好”的可重用的。例如,在网络出现问题时,您可以使用诸如polly之类的库来重复请求。在那种情况下,您需要您的代码引发异常,以便波莉或其他库可以处理它。


1
同意,默认代码缺少该功能,无法从返回消息中获取有意义的消息。
LT 2015年

2
您的版本与的原始实现不同EnsureSuccessStatusCode。您总是要处置response.Content(因为即使在该return;语句之后,总会始终调用它),它会破坏内容以供进一步阅读。原始实现仅在状态代码未指示成功结果时才处理内容。
Lukas.Navratil 2015年

4
我不知道为什么要先await response.Content.ReadAsStringAsync()查询然后再查询if (response.Content != null)
mafu

3
现在,Polly处理返回结果以及异常,正是为了帮助解决这种情况。您可以将Polly配置为保护HttpRequest呼叫,并配置策略以处理某些异常和某些HttpResponseCode。请参阅此处的Polly自述文件中
登山旅行者

2
response.Content当刚刚调用了一个方法时,怎么可能为null?
伊恩·沃伯顿

1

当我不想在同一方法上处理Exception时,可以使用GuaranteeSuccessStatusCode。

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上处理。


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

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)
                );
        }
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.