Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change EnsureSuccessStatusCode to not dispose response content #24845

Closed
thomaslevesque opened this issue Jan 30, 2018 · 7 comments
Closed

Change EnsureSuccessStatusCode to not dispose response content #24845

thomaslevesque opened this issue Jan 30, 2018 · 7 comments
Assignees
Labels
area-System.Net.Http enhancement Product code improvement that does NOT require public API changes/additions
Milestone

Comments

@thomaslevesque
Copy link
Member

Currently the HttpResponseMessage.EnsureSuccessStatusCode method always disposes the content of the response. The reason is explained in a comment:

// 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.

However, this isn't always helpful. It's frequent for web APIs to return details about the error in the response body. If I want to map the error to a specific domain exception, I might write something along these lines:

private async Task<T> GetResponseAsync<T>(HttpRequestMessage request)
{
    using (var response = await _client.SendAsync(request))
    {
        try
        {
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsAsync<T>();
        }
        catch (HttpRequestException ex)
        {
            var errorDetails = await response.Content.ReadAsAsync<ErrorDetails>();
            throw CreateDomainExceptionFromDetails(errorDetails, ex);
        }
    }
}

private Exception CreateDomainExceptionFromDetails(ErrorDetails errorDetails, Exception innerException)
{
    // Some logic based on the error details
    // ...
}

However, this doesn't work, because when I try to read the content in the catch block, the content is already disposed. So I need to read the content in the try block instead, before the call to EnsureSuccessStatusCode:

private async Task<T> GetResponseAsync<T>(HttpRequestMessage request)
{
    using (var response = await _client.SendAsync(request))
    {
        string content = null;
        try
        {
            content = await response.Content.ReadAsStringAsync();
            response.EnsureSuccessStatusCode();
            return JsonConvert.DeserializeObject<T>(content);
        }
        catch (HttpRequestException ex) when (content != null)
        {
            var errorDetails = JsonConvert.DeserializeObject<ErrorDetails>(content);
            throw CreateDomainExceptionFromDetails(errorDetails, ex);
        }
    }
}

It's less intuitive and makes the code harder to read

An EnsureSuccessStatusCode(bool disposeContent) overload would be helpful in this case. It would let the user state that they're taking responsibility for disposing the content (in the exemple above, the content would be disposed when the response is disposed at the end of the using statement).

(I'm aware that I could just check the IsSuccessStatusCode property, but I want the "normal" HttpRequestException as the inner exception of my domain exception)

I'm willing to submit a pull request if the proposal is approved.

@davidsh
Copy link
Contributor

davidsh commented Mar 22, 2018

cc; @stephentoub @karelz

We should make this change. It only made sense for EnsureSuccessStatusCode to dispose the request content when HttpClient was doing it while sending requests.

However, we changed that behavior in PR dotnet/corefx#19082

So, to be consistent, we need to fix EnsureSuccessStatusCode() as well. And by "fix" this, I mean that we should just change the behavior of the EnsureSuccessStatusCode API. We don't need to add a new overload.

@stephentoub
Copy link
Member

We should make this change. It only made sense for EnsureSuccessStatusCode to dispose the request content when HttpClient was doing it while sending requests.

I'm fine with changing EnsureSuccessStatusCode, but isn't it disposing of the response content, not the request content?

@davidsh
Copy link
Contributor

davidsh commented Mar 22, 2018

Ugh...you're right. I read this wrong. I thought it was referring to request content. :(

But in reviewing, we probably should still change this here to follow the same principles we did in #21394 and not proactively dispose something.

I would still be in favor of not doing an explicit Dispose() here of the response body content. It would get disposed later anyways when the enclosing HttpResponseMessage gets disposed.

@stephentoub
Copy link
Member

SGTM

@davidsh davidsh changed the title Proposal: add an overload of EnsureSuccessStatusCode that doesn't dispose the content Change EnsureSuccessStatusCode to not dispose response content May 11, 2018
@davidsh davidsh self-assigned this May 11, 2018
stephentoub referenced this issue in dotnet/corefx May 19, 2018
* Change EnsureSuccessStatusCode to not dispose response content

Historically, this method has always disposed the response content if it was
throwing an exception. The reason documented in the comment really doesn't
make much sense, especially to just dispose the content but not the actual
response object. The developer pattern we advise when getting an HttpResponseMessage
is to wrap it in a 'using' statement or use an explicit .Dispose() call. And
disposing the HttpResponseMessage would always dispose the inner .Content
anyways.

The use case of #26684 is that the response content should be available regardless
if this method throws an exception or not. Developers normally don't expect an object
to get disposed (partially in this case) as a side-affect of calling a method on the
object. So, we're changing the behavior to be more consistent generally with other
.NET objects.

Fixes #26684

* Address PR feedback
@karlzf
Copy link

karlzf commented Feb 16, 2019

I do not see the fix applied to .NET Core 2.2 release. Can anyone confirm? Thanks. https://github.com/dotnet/corefx/blob/release/2.2/src/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs

@nbarbettini
Copy link
Member

@karlzf Confirmed, .NET Core 2.2 still disposes the Content property.

For some reason the activity log above doesn't show that it was re-assigned to the 3.0 milestone, but the "Milestone" field at the top of the page shows 3.0.

@davidsh
Copy link
Contributor

davidsh commented Jul 16, 2019

For some reason the activity log above doesn't show that it was re-assigned to the 3.0 milestone, but the "Milestone" field at the top of the page shows 3.0.

This fix is only in the .NET Core 3.0 branch.

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 3.0 milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 18, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Http enhancement Product code improvement that does NOT require public API changes/additions
Projects
None yet
Development

No branches or pull requests

6 participants