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.
Several months ago, Microsoft decided to change up the HttpResponseMessage class. Before, you could simply pass a data type into the constructor, and then return the message with that data, but not anymore. Now, you need to use the Content property to set the content of the message.
A HttpResponseMessage allows us to work with the HTTP protocol (for example, with the headers property) and unifies our return type. In simple words an HttpResponseMessage is a way of returning a message/data from your action.
HttpRequestException(String, Exception) Initializes a new instance of the HttpRequestException class with a specific message that describes the current exception and an inner exception. HttpRequestException(String, Exception, Nullable<HttpStatusCode>)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With