refit
refit copied to clipboard
[BUG] When response deserialization fails, Content is null in ApiException
Describe the bug
When Refit can't deserialize response from api, ApiException is thrown. Unfortunately, Content property is always null, but it should be populated with the response content, that can't be deserialized.
Steps To Reproduce
Reproduction with simple XUnit test (api can be arbitrary, here I've used first public api I've found):
using System.Threading.Tasks;
using Refit;
using Xunit;
namespace RefitReproduction
{
public class RefitNullContentTest
{
[Fact]
public async Task ShouldHaveContentInException()
{
var api = RestService.For<IApi>("https://api.spacexdata.com");
var exception = await Assert.ThrowsAsync<ApiException>(() => api.GetLaunches());
Assert.NotNull(exception.Content); // fails!
}
}
public interface IApi
{
[Get("/v3/launches")]
public Task<double> GetLaunches(); // any type not matching api response
}
}
Expected behavior
When deserialization fails, Content property should be populated with response content.
Environment
- OS: Windows
- Device: PC
- .NET version: 6.0
- Refit version: 6.3.2
I've found the source of bug:
ApiException.cs, line 132:
try
{
exception.ContentHeaders = response.Content.Headers;
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
exception.Content = content;
if (response.Content.Headers?.ContentType?.MediaType?.Equals("application/problem+json") ?? false)
{
exception = ValidationApiException.Create(exception);
}
response.Content.Dispose();
}
catch
{
// NB: We're already handling an exception at this point,
// so we want to make sure we don't throw another one
// that hides the real error.
}
response.Content.ReadAsStringAsync()
throws System.InvalidOperationException: 'The stream was already consumed. It cannot be read again.'
Anyone knows how to fix it?
So, any solutions now?
Hi, were you able to figure out a solution to this problem?
I just ran into this myself -- I found a stopgap solution in the interim while Dwolla gets around to resolving this which unfortunately judging by the timestamps on comments on this thread could be awhile. 😞 I just manually deserialize the RawContent property into the expected type myself. The below example is utilizing Newtonsoft.Json for the deserialization and Dwolla.Client v6.0.0:
// Omitted setting up uri and headers for brevity. var result = await client.GetAsync<BalanceResponse>(uri, headers); // Omitted response error checking for brevity. return JsonConvert.DeserializeObject<BalanceResponse>(result.RawContent);