I'm using HttpClient
to send requests to my API project, the problem is that the GetFromJsonAsync
method fails to parse the response JSON, but if I first get the response using the GetAsync
method, and then do a ReadFromJsonAsync
on that, it'll parse the JSON successfully, why is that?
This code doesn't work:
public async Task<OperationResult<LoginNextStep>> SearchByEmailOrPhone(string emailOrPhone)
{
// This fails to parse the response body
var result = await _client.GetFromJsonAsync<ApiResult<OperationResult<LoginNextStep>>>
($"api/user/searchbyemailorphone/{emailOrPhone}", _jsonOptions);
return result.Data;
}
This one does:
public async Task<OperationResult<LoginNextStep>> SearchByEmailOrPhone(string emailOrPhone)
{
var result = await _client.GetAsync($"api/user/searchbyemailorphone/{emailOrPhone}");
// But this one works, with the same JSON
var jsonResult = await result.Content.ReadFromJsonAsync
<ApiResult<OperationResult<LoginNextStep>>>(_jsonOptions);
return jsonResult.Data;
}
This is the _jsonOptions
passed into the methods (Injected through the constructor into the class):
services.AddSingleton(new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter() },
PropertyNameCaseInsensitive = true
});
EDIT:
I forgot to say this, this only happens with a specific JSON returned from the API, I mean it's the same type as ApiResutl
but for some weird reasons it fails to parse it, and in my API project, I'm using the AspNetCoreRateLimit
package to throttle the spam requests, and in that package, if it decides to drop a request, it would by default return a line of text, but I could overwrite that and I added my own return type when it drops a request, and it's an ApiResult
which is the default return type of all of my API's endpoints, (so I wanted that package to return the same result as other endpoints with 429 status code to indicate TooManyRequests).
This is the overwritten return type when it drops a spam request:
public class CustomIpRateLimitMiddleware : IpRateLimitMiddleware
{
public CustomIpRateLimitMiddleware(RequestDelegate next, IProcessingStrategy processingStrategy,
IOptions<IpRateLimitOptions> options, IIpPolicyStore policyStore, IRateLimitConfiguration config,
ILogger<IpRateLimitMiddleware> logger) : base(next, processingStrategy, options, policyStore, config, logger)
{
}
public override async Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitRule rule,
string retryAfter)
{
var result = new ApiResult
{
IsSuccessful = false,
MetaData = new MetaData
{
ApiStatusCode = ApiStatusCode.TooManyRequests,
Message = "You sent too many requests!!!"
}
};
var jsonOptions = new JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } };
var jsonResult = JsonSerializer.Serialize(result, jsonOptions);
httpContext.Response.Headers["Retry-After"] = retryAfter;
httpContext.Response.StatusCode = (int)ApiStatusCode.TooManyRequests;
httpContext.Response.ContentType = "application/json";
await httpContext.Response.WriteAsync(jsonResult);
//return base.ReturnQuotaExceededResponse(httpContext, rule, retryAfter);
}
}
To produce the issue, first I manually try to spam and the API will throttle my requests:
And then this is the response body as string, which is the same as the picture above:
The ReadFromJsonAsync
could parse the JSON:
But the same request with GetFromJsonAsync
fails, and it throws an HttpRequestException
exception, (I think I did the same thing yesterday and it would throw an exception like CouldNotParseJson to ApiResult type