-
-
Notifications
You must be signed in to change notification settings - Fork 746
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
Content is null when ApiResponse followed by EnsureSucessStatusCodeAsync() #558
Comments
Can you try the latest version from the MyGet feed? There is now an Error property on the ApiResponse that contains the |
Will do |
Sorry, what is the url for the refit MyGet feed? Search at https://docs.myget.org for refit but could not find anything. Apologies if a dumb question but never used MyGet before!
|
Sorry, I thought it was in the readme...but it's not. I'll file an issue for that. The myget feed is here: |
Hey Oren, Super little library!! Well done. I was looking at the code for this and it does appear that Refit calls dispose on objects it didn't create so I think that's the root cause. Could you consider whether you think this is the best design? It's quite well-known not to dispose of objects you didn't expressly create, else you're inviting trouble. https://blogs.msdn.microsoft.com/pfxteam/2009/06/24/dont-dispose-of-objects-that-you-dont-own/ Moreover, from what I can tell, if the response is successful then the "request stack" isn't disposed, its left to the caller to have wrapped the call in a If this is the case, my analysis could be wrong, but why worry about disposal if there's an unsuccessful status code but not if successful? My thinking is that the request still needs to live on for as long as needed to pull further information "off the wire" and potentially drain the bytes from the server. We must remember, afterall, a successful HTTP/TCP conversation took place, only that there was a number >= 400 in some message text. Cheers!
|
Hi, I'm not sure where you see that we are disposing of objects we didn't create? We create and own the lifetime of the HttpClient pipeline except in a few narrow cases -- returning HttpResponseMessage or Stream types. As ApiResponse is disposable, I would expect a caller to always dispose it. |
Hey thanks for prompt reply EnsureSuccessStatusCodeAsync It disposes of itself rather than letting the consumer of the class do it. It's like it's saying, well, I've thrown an exception I better kill myself, but that doesn't make sense to me, the decision to clear down is upon the holder of that ApiResponse instance. |
I'm not completely sure why that is there without digging further, but in that code path, there's nothing more you can do with the response. The ApiException reads the response content as a string and makes it available either as strongly typed property or as a string. I'm not sure what behavior you're seeing that's not working as expected? |
"there's nothing more you can do with the response" So we need to pull out the error detail from the payload but its disposed. Do you see? There is potentially an entire content stream to suck down the pipe once we have an exception :) |
Thanks for you time, btw. We were discussing at work what I nightmare it must be to have a really successful OSS project. |
In that error case, the error stream is already read in as the content of the ApiException, that's why there's nothing further to read? |
Ah. Well that's where the code conspires with the other code to create the bug. See the comment by the OP.
The root cause is the self-disposal. I think its really odd to self-dispose. To my mind, the Dispose pattern is there for consumers. If you implement Else if you're going to encapsulate clearing down nicely, then don't implement At present you have this situation where you're saying "hey I implement IDisposable, you might wanna dispose of me... err, unless you call this method in which case I secretly dispose for you." Another way to think of it is that classes should throw try
{
await apiResponse.EnsureSuccessStatusCodeAsync();
return "Everything's good, man.";
}
catch (ApiException apiex)
{
return "Oh no, I got a " + apiResponse.ReasonPhrase; // ObjectDisposedException
} Does that make sense? I'm not stubborn, I could be mad. I'd love someone else's opinion. |
Shall we reopen this and see if we can get more eyes on it? |
Re-opening as that doesn't look right. We probably need a few more tests around That said, this isn't about disposable. Refit is designed as primarily a wrapper around the underlying httpclient types; the consumer should not be worrying about them. |
@lukepuplett, Is there any workaround you are doing for this, facing the same case as you. |
This works for me though.
|
@acclaimuser Unfortunately I'm no longer with that team, so I can't look-up any workaround code cc @Dilsy99 |
Just ran into this myself |
Closing due to age. Please try Refit v6 and reopen if still an issue. |
v6 has the same error, Luke is right, Refit should not dispose the response content as a consumer. public async Task<ApiResponse<T>> EnsureSuccessStatusCodeAsync()
{
if (!IsSuccessStatusCode)
{
// Add this "if" is a simple fix
if (Error is not null)
{
throw Error;
}
var exception = await ApiException.Create(response.RequestMessage!, response.RequestMessage!.Method, response, Settings).ConfigureAwait(false);
Dispose();
throw exception;
}
return this;
}` |
#1189 is also related to this issue. |
Hi everyone! I'm experiencing something that I think is related to this issue. When I call an API using Refit that responds a non-200 status code, and call string responseContent = null;
string responseContentFromApiException = null;
try
{
var apiResponse = await ... ; // RefitCall
responseContent = apiResponse.Error?.Content; // has value
await apiResponse.EnsureSuccessStatusCodeAsync();
}
catch (ApiException ex)
{
responseContentFromApiException = ex.Content; // is null
} Windows 10 |
The issue still happens on Refit 6.3.2 Indeed the solution is to check if Error is null.
|
Hi,
I have noticed an issue with refit when getting a response wrapped in a refit ApiResponse which them calls EnsureSuccessStatusCodeAsync()
The ApiException thrown by the EnsureSuccessStatusCodeAsync() has null content even when content exists in the httpResponseMessage.
This sort of code:
The issue seems to be caused because ApiException disposes of the response content
In RequestBuilderImplementation, on error for an ApiResponse response, it created an ApiException which is passed in to the ApiResponse
The call to EnsureSuccessStatusCodeAsync() then creates another ApiException (if not successful). However because the response content has been disposed by the previous ApiException creation, the content is now null.
There seems to be a couple of ways of resolving this.
I am happy to make the changes but will be guided by your suggestions for the best way.
Many thanks.
Core 2.1
Refit 4.6.30
Visual Studio 2017 v4.7.03056
Windows 10
The text was updated successfully, but these errors were encountered: