HttpClient not downloading the entire JSON - c#

I have an app on Windows Phone Store, it's a Feedly client and some of my users have been reporting an error for a while.
The error is a JsonReaderException: Unterminated string. Expected delimiter: ". Path 'items[0].summary.content', line 1, position 702
Looking at the error, it seems that the HttpClient didn't download the entire Json, since the position is the end of the response and the response seems incomplete.
Here is one of the responses:
{
"id":"user/{userIdOmmited}/category/global.all",
"updated":1417324466038,
"continuation":"149ebfc5c13:c446de6:113fbbc6",
"items": [{
"id":"HBKNOlrSqigutJYKcZCnF5drtVL1uLeqMvamlHXyreE=_149ff1f0f76:213a17:34628bd3",
"fingerprint":"eb0dc432",
"originId":"https://medium.com/p/7948bfedb1bc",
"updated":1417324463000,
"title":"Iran’s Stealth Drone Claims Are Total BS",
"published":1417324463000,"crawled":1417324466038,
"alternate":[{
"href":"https://medium.com/war-is-boring/irans-stealth-drone-claims-are-total-bs-7948bfedb1bc",
"type":"text/html"
}],
"summary":{
"content":"<div><p><a href=\"https://medium.com/war-is-boring/irans-stealth-drone-claims-are-total-bs-7948bfedb1bc\"><img height=\"200
This is the entire Json of one of the responses, as you can see it ends suddenly at the summary.content, that's why Json.Net can't deserialize it.
My Get method looks like this:
protected async Task<T> GetRequest<T>(string url)
{
var handler = new HttpClientHandler();
if (handler.SupportsAutomaticDecompression)
handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;
using (var client = new HttpClient(handler))
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
if (authentication != null)
request.Headers.Authorization = authentication;
var result = await client.SendAsync(request);
result.EnsureSuccessStatusCode();
var data = await result.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(data.EscapeJson());
}
}
I pass the response DTO as a generics parameter to the method and it deserializes the Json.
The EscapeJson method in the return looks like this:
public static string EscapeJson(this string stringToEscape)
{
return Regex.Replace(stringToEscape, #"(?<!\\)\\(?!"")(?!n)(?!\\)", #"\\", RegexOptions.IgnorePatternWhitespace);
}
I've added this to try to solve the problem because I thought the problem was with the back slashes, but it wasn't (before I found out the json wasn't being downloaded completely).
I've been searching for a solution for this problem for a few weeks, and I couldn't come up with an answer.
In my research I found out that there is a parameter in the SendAsync that is the completionOption, which is an enum, HttpCompletionOption, that has two options: ResponseContentRead and ResponseHeadersRead.
The problem is that I don't know which one is the default and I don't know if changing this will solve the problem since I can't reproduce the problem myself, so I can't test it.
Does anyone has an idea of what might be the problem here?
Could it be a Timeout of sorts or this HttpCompletionOption?
I've been seeing the error for a while, searching for an answer and I have no clue on what might be going on.
Thanks in advance for the help!

Make one HttpClient for the app to use. Do not dispose it. HttpClient is supposed to be reused and not disposed pr request. That is most likely the error.

Related

Restsharp: An error occurred while sending the request, The response ended prematurely

I know that stack overflow is full of problems like this but i have not found answer in the other ones, also, my problem is really weird.
We have an integration with a variety of WooComerce/Wordpress shops.
I have an only function for every task that i do in all the pages and it is later called whit the data of the differents markets, for example, this is the method to get all the orders "Is a simple GET request using RestSharp":
public static async Task<List<string>> GetOrdersWooComerce(string urlinicio, string secreto, string cliente)
{
var url = urlinicio+"/wp-json/wc/v3/orders?consumer_key="+cliente+"&consumer_secret="+secreto+"&status=processing";
var client = new RestClient(url);
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
JArray jsongo = JArray.Parse(response.Content);
List<string> keys = new List<string>();
foreach (var j in jsongo.AsJEnumerable())
{
keys.Add(j["id"] + "");
}
return keys;
}
It works perfectly with all the shops except one that returns me an empty content with the next exception:
I only am using an url that returns a json, i dont know where is the error in that simple request... also if i copy the url and paste it in postman it works.
¡Even in the google search it returns me the json! Is as simple as that, only an url, but in my code doesn't work only with that shop :(

Why is the receiving API not getting content in the parameter?

I am trying to get one API (reports API) to talk to another API (stored procedure runner API) which is a solution we are sort of forced to adopt given that we created the first API in .NET Core 2.2 and the Sap.Data.SQLAnywhere.v4.5 drivers only play nice with the .NET 4.7.2 framework. So we segregated them to compensate. All the answers I have seen over the last 4 or 5 hours leads me to believe I am doing this correctly but it still doesn't work.
I can hit the stored procedure runner from Postman with a json/text body just fine and get results from the database. However, when I try to hit this from C# I was first getting Unsupported Media Type which I think I fixed but now I get 500 errors and when debugging through to the stored procedure runner from the reportsAPI I notice that I don't get a parameter passed from the body.
[HttpPost]
[Route("api/Reports/GetStuff")]
[ResponseType(typeof(ResponseObject))]
public ResponseObject GetStuff([FromBody]string report)
{
var response = new ResponseObject();
try
{
response = new ReportService().RunStuff(report);
}
catch (Exception e)
{
throw;
}
return response;
}
The above is the SprocRunnerAPI and I get as far as
response = new ReportService().RunStuff(report);
before it fails out because it has nothing in "report".
public class ApiService
{
public static HttpClient ApiClient { get; set; }
public static void InitializeClient()
{
ApiClient = new HttpClient();
//ApiClient.BaseAddress = new Uri("");
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
This is where I initialize everything then use it in the following class method:
public ResponseObject RunReportToCSV(string report)
{
var rep = new ResponseObject();
ApiClient.ApiService.InitializeClient();
string url = #"http://localhost/EnterpriseReportRunner/api/reports/GetStuff";
var httpContent = new StringContent(report, Encoding.UTF8, "application/json");
//ServicePointManager.Expect100Continue = false; I honestly don't know where this goes... or if it is needed.
var response = ApiClient.ApiService.ApiClient.PostAsync(url , httpContent).Result;
if (response.IsSuccessStatusCode)
{
rep = response.Content.ReadAsAsync<ResponseObject>().Result;
}
else
{
throw new Exception(response.ReasonPhrase);
}
return rep;
}
I get as far as
var response = ApiClient.ApiService.ApiClient.PostAsync(url, httpContent).Result;
when it calls the aforementioned api method externally and it fails out. I had noticed no differences between the bodies in what I put in C# and what I put in Postman nor did I notice a difference looking through Fiddler on the Headers when making either call. I am seriously confused as to why what appears to be what I have seen everywhere used, that this is not working.
Thank you.
Adding Stack Trace from the SprocRunnerAPI… this isn't much help because I know exactly why this happens. By this point I expect a JsonSerialized object and I don't have it because it wasn't passed from the first API to the second.
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at EnterpriseReportRunner.Service.ReportService.RunStuff(String report) in C:\Workspace\Solution.NET\LOB\InternalReportsAdminConsole Solution\EnterpriseReportRunner\EnterpriseReportRunner\Service\ReportService.cs:line 27
at EnterpriseReportRunner.Controllers.ReportsController.GetStuff(String report) in C:\Workspace\Solution.NET\LOB\InternalReportsAdminConsole Solution\EnterpriseReportRunner\EnterpriseReportRunner\Controllers\ReportsController.cs:line 67
To be clear this is the stack from the SprocRunnerAPI not the ReportsAPI that calls it. I am trying to find the body inside the HttpContext to see the difference between the Postman call and the C# call but can't seem to find it, is it really buried in the InputStream?
The issue seems to be one that I thought would be redundant. The bottom line is that even if you expect a serialized JSON object you need to serialize it again, otherwise when you pass the StringContent via the HttpClient the body contents don't show in the receiving controller's parameter for consumption.
var httpContent = new StringContent(JsonConvert.SerializeObject(report), Encoding.UTF8, "application/json");
Even though "report" is already a string... really weird to me. But that solves it.
Found out that the strings that come into a controller are also wrapped inside another object so .Root seems to be your friend when deserializing. At least I learned something from the experience.
Thank you for all your input. Might want to link this to this other question.

HTTP Post Request from C# to Flask REST API won't attach any parameters?

I have been attempting to do a simple POST request to a basic Flask API, and no matter what I try it simply will do http://localhost:5000/ rather than the http://localhost/?test_key=test_value that I want it to do. C# is not my strongest language, but I have tried a bunch of different methods that I found online. I don't think it has to do with my method being bad at this point, I more so think that I'm missing a major piece entirely.
Here is the most recent attempt I tried (keep in mind this is a snippet of a far larger project, but it doesn't involve any other pieces of it):
class DisputeResponse
{
public int id;
public string res;
public string test_key;
}
[HttpPost]
public async Task<JsonResult> TestResponse(int id, string res)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:5000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
try
{
var dispRes = new DisputeResponse();
dispRes.test_key = "test_value";
var result = await client.PostAsync("http://localhost:5000/",
new StringContent(JsonConvert.SerializeObject(dispRes), Encoding.UTF8, "application/json"
));
result.EnsureSuccessStatusCode();
}
catch (Exception e)
{
System.Diagnostics.Trace.TraceInformation(e.Message);
}
return null;
}
Here is the output when I use Postman (works perfectly):
127.0.0.1 - - [15/Apr/2019 16:26:28] "POST /?test_key=test_value HTTP/1.1" 200 -
And here is when I try to use C# code:
127.0.0.1 - - [15/Apr/2019 16:36:54] "POST / HTTP/1.1" 500 -
EDIT: Sorry I had some extraneous lines that I removed.
The HTTPClient is putting the parameters inside the body of the request. If you would like to add it to the URI you can do so by modifying the URI string. If you are not posting anything to the body, I would modify the FLASK API to receive a GET instead of a POST.
Typically I would only use query string parameters in the URI string for HTTPGet requests, even though there is no rule stopping you from doing this with a HTTPPost. Additionally, I would only include my parameters for an HTTPPost request within the body. Again, no rule for this but just what I do.
Here are a few examples to help you along with the HTTPClient post async method:
https://csharp.hotexamples.com/examples/-/HttpClient/PostAsync/php-httpclient-postasync-method-examples.html

HttpResponseMessage System.InvalidOperationException

Quite a simple problem I've got that would be easy to solve I reckon for the brains. It's just a tried a different queries on Google and nothing popped up but hey that's why I am here.
Here is the error:
System.InvalidOperationException
Basically this error is thrown on this piece of code here
string test = response.Content.Headers.GetValues("Location").FirstOrDefault();
Location in Fiddler looks like this:
Location: https://www.redirecturlishere.com/blah
Here is the entire function:
private async Task<string> GetRequest()
{
//HttpContent postContent = new StringContent(post, Encoding.ASCII, "application/x-www-form-urlencoded");
using (HttpResponseMessage response = await httpClient.GetAsync(
"http://www.cant-share-url.com"))
{
using (HttpContent content = response.Content)
{
string test = response.Content.Headers.GetValues("Location").FirstOrDefault();
}
}
return "";
}
More details on the error, "Additional information: Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."
I don't think there is much else to explain so I'll leave it at that.
I have solved the problem basically the header was not stored in the content, silly me.
And for anyone who doesn't see the solution. :)
string test = response.Headers.GetValues("Location").FirstOrDefault()

How to write nested Json web services in c# for windows store app

I'm working on windows store app where i am using a web service which have parameter which are nested like this
userData, #"user_login",
email, #"email",password, #"password",
user_login key contains to key email and password with their value .I Hit the web service in the fiddler and if works i am getting the response the parameter string i used
{
"user_login":{"email" : "mmmmmmmm#fdhfgh.co","password" : "pass1234"}
}
when i am using this parameter string in my code i am getting error of status code 400 bad request due to the parameter string .
here is my code
string content = string.Empty;
System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient();
httpClient.DefaultRequestHeaders.Date = DateTime.Now;
var httpContent = new System.Net.Http.StringContent("{'user_login':[{'email':"mmmmmmmm#fdhfgh.co",'password':'pass1234'}}]", Encoding.UTF8,"application/json");
var httpResponse = await httpClient.PostAsync(" http://localhost/appi/pupil/log_in", httpContent);
content = await httpResponse.Content.ReadAsStringAsync();
help me out with your valuable suggestions.
In your C# code, you're passing in an array (as denoted by the [ ]'s). It doesn't look like your were doing that in Fiddler. It also looks like the [ ]'s aren't formatted properly. I'd try just taking that out.
EDIT:
"{\"user_login\":{\"email\":\"mmmmmmmm#fdhfgh.com\",\"password\":\"pass1234\"}}"
If this is the correct signature, the above JSON would work.
EDIT 2:
var httpResponse = await httpClient.PostAsync(" http://localhost/appi/pupil/log_in", httpContent);
Couldn't help but notice that you had "localhost/appi" instead of "localhost/api". That could possibly be a typo? Along with that leading space.

Categories