7

I have a service which is consuming an SMS REST API using HttpClient:

HttpClient http = this._httpClientFactory.CreateClient();
// Skipped: setup HttpRequestMessage
using (HttpResponseMessage response = await http.SendAsync(request))
{
    try
    {
        _ = response.EnsureSuccessStatusCode();
    }
    catch (HttpRequestException)
    {
        string responseString = await response.Content.ReadAsStringAsync(); // Fails with ObjectDisposedException
        this._logger.LogInformation(
            "Received invalid HTTP response status '{0}' from SMS API. Response content was {1}.",
            (int)response.StatusCode,
            responseString
        );
        throw;
    }
}

The API returns an error, but I would like to be able to log it. So I need to log both the failing status code (which I can read from response.StatusCode) and the associated content (which may contain additional error useful details).

This code fails on the instruction await response.Content.ReadAsStringAsync() with this exception:

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Http.HttpConnection+HttpConnectionResponseContent'.
    Module "System.Net.Http.HttpContent", in CheckDisposed
    Module "System.Net.Http.HttpContent", in ReadAsStringAsync

Some sources suggest that you shouldn't read the response content when the status code is not in the success range (200-299), but what if the response really contains useful error details?

.NET version used: .NET Core 2.1.12 on AWS lambda linux runtime.

2
  • Why not read the content before throwing the exception? Then pass the content to the exception so you can access it in your catch block. Commented Jan 10, 2020 at 9:49
  • Are you implying EnsureSuccessStatusCode() is disposing the response? Commented Jan 10, 2020 at 9:51

1 Answer 1

9

OK, apparently this is a known issue in the .NET API, which has been addressed in .NET Core 3.0. response.EnsureSuccessStatusCode() is actually disposing the response content. It was implemented this way to supposedly help users:

// Disposing the content should help users: If users call EnsureSuccessStatusCode(), an exception is
// thrown if the response status code is != 2xx. I.e. the behavior is similar to a failed request (e.g.
// connection failure). Users don't expect to dispose the content in this case: If an exception is
// thrown, the object is responsible fore cleaning up its state.

This is an undesirable behavior which was removed from 3.0. In the meantime, I just switched to use IsSuccessStatusCode before the log:

HttpClient http = this._httpClientFactory.CreateClient();
// Skipped: setup HttpRequestMessage
using (HttpResponseMessage response = await http.SendAsync(request))
{
    if (!response.IsSuccessStatusCode)
    {
        string responseString = await response.Content.ReadAsStringAsync(); // Fails with ObjectDisposedException
        this._logger.LogInformation(
            "Received invalid HTTP response status '{0}' from SMS API. Response content was {1}.",
            (int)response.StatusCode,
            responseString
        );
        _ = response.EnsureSuccessStatusCode();
    }
}

A little bit more redundant, but it should work.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.