HttpClient ReadAsAsync does not deserialize - c#

I am not sure if the issue I am having is related to the way I'm using Task or if I am an not using ReadAsAsync correctly. I am following the pattern I found here:
http://blogs.msdn.com/b/henrikn/archive/2012/02/11/httpclient-is-here.aspx
Background:
Object I am deserializing is a POCO. Properties have no attributes. It is just a few value type properties and a couple collection properties. REST service appears to work ok also. When I look at the JSON returned by the service it appears to be OK.
Using Web API 2.1 5.1.2
Problem:
.. is calling HttpResponseMessage.Content.ReadAsAsync(). Sometimes it works (returns an object) and sometimes it doesn't (throws "Thread was being aborted" or returns null). It appears the content property can be read once only and subsequent reads throw errors. See comments in code below.
Related questions:
HttpContent.ReadAsAsync Deserialization issue
Question appears to be similar to mine. Answer indicates a bug but this is over two years old.
Code:
[TestMethod]
public void AddSiteTest()
{
// Use POST to create a resource i.e. insert. Use PUT to update.
Site site = new Site {SiteName = "Test", Active = true, URI="www.test.com" };
Site newSite = null;
client.PostAsJsonAsync<Site>(baseURI + "/Sites/AddSite?securityKey="+ SecurityKey, site).ContinueWith(x =>
{
HttpResponseMessage response = x.Result;
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
try
{
string str = Task.Run(() => response.Content.ReadAsStringAsync()).Result; // yep its json and it is a proprety serialized object
// Method 1 (preferred... ):
//Site siteA = Task.Run(() => response.Content.ReadAsAsync<Site>()).Result; // usuallly throws if content has been read
// Method 2:
Site siteB = response.Content.ReadAsAsync<Site>().Result; // usully returns a valid result (when I dont put a breakpoint on it). Does not deadlock.
// Method 3:
response.Content.ReadAsAsync<Site>().ContinueWith(d =>
{
Site siteC = d.Result; // returns null
});
}
catch (Exception ex)
{
string y = ex.Message;
}
}
});
}

try to use await:
string str = await response.Content.ReadAsStringAsync();
And you have to add async before void in your method.

Related

Make function more Generic to save repeating

I use the following function which is all well and fine but i basically do the same operation about 20 times. For various end points of an api I am hitting how would one make this routing more Generic in the ability to pass and return type OF T.
public async Task<List<StockItem>> GetStockDataFromSage()
{
StockItem stockitems = new StockItem();
string content = "";
List<StockItem> result = new List<StockItem>();
var uri = new Uri(string.Format(Constants.GetStockItems, string.Empty));
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
result = Newtonsoft.Json.JsonConvert.DeserializeObject<List<StockItem>>(content);
}
return result;
}
Edit 1
I am trying to use the below however I am getting an error
public async Task<List<StockItem>> GetStockItemInfo()
{
return await dataTransfer.GetDataFromSageService(Constants.GetStockItems, string.Empty)) ?? new List<StockItem>();
}
Severity Code Description Project File Line Suppression State
Error CS1061 'StockTakeDT' does not contain a definition for 'GetStockDataFromSage' and no accessible extension method 'GetStockDataFromSage' accepting a first argument of type 'StockTakeDT' could be found (are you missing a using directive or an assembly reference?) StockAppDL D:\Git\Repos\StockApp\FStockApp\StockAppDal\StockDatabase.cs 76 Active
Your objective here appears to be to call an endpoint and get the results back into an object you can use. If the call is successful, you return the result and if it fails, you return an empty list.
We can abstract that logic out into a generic method that accepts a url and parameters and returns an object.
public async Task<T> GetObjectFromEndpoint<T>(string url, params string[] args)
where T : class
{
var uri = new Uri(string.Format(url, args));
var response = await _client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(content);
}
return default(T);
}
Now your GetStockDataFromSage function passes in the information unique to this call, namely the url, parameters, and generic type for the results. If the result is null, GetStockDataFromSage returns an empty list of StockItems
public async Task<List<StockItem>> GetStockDataFromSage()
{
return (await GetObjectFromEndpoint<List<StockItem>>(Constants.GetStockItems, string.Empty)) ?? new List<StockItem>();
}
Any time you are trying to minimize repetition, you want to look at what is specific to this call and what is more general. i.e List<StockItem>, the url, and possibly the parameter are unique, but the rest of the code is very general.
Caution: This method of returning a default value when the api call fails can lead to hard-to-diagnose issues where you will be unable to differentiate between an empty list and a failed api call. I recommend adding some logging for failed api calls and perhaps looking at ways to inform the calling code that the result was in error.

C# BotFramework FormFlow, pass values to an external api

I am currently developing a simple c# formflow bot that captures the values and sends those values off to an external api, gets the json data back from the external api and creates Card Attachments based on the results returned. I am making the call to the external api in the OnCompletion delegate as follows, To keep it simple I am not passing any values to the api (For testing purposes)
.OnCompletion(async (context, profileForm) =>
{
var reply = context.MakeMessage();
var carsFromApi = await GetCarsAsync("/api/values");
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments = GetCards(carsFromApi);
await context.PostAsync(reply);
// Tell the user that the form is complete
})
I make the call to the api and store the results in "carsFromApi" , I step into that which is the following code snippet
private static async Task<List<Car>> GetCarsAsync(string path)
{
List<Car> car = new List<Car>();
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
car = await response.Content.ReadAsAsync<List<Car>>();
}
return await response.Content.ReadAsAsync<List<Car>>();
}
Problem is when I press F10 and go to the next line which is "reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;". The local variable that stored the cars "carsFromApi " is now null. This is the part where it all falls over. I cant pass this "carsFromApi" to "reply.Attachments = GetCards(carsFromApi);" I have tried to store the data in a private variable but that also seems to be null. The external api is working because it just returns a list of static text for now. Any ideas? Thanks in advance.
Based on what you are describing it sounds that your code is not existing through the path of the if (response.IsSuccessStatusCode). Check if that point is reached as I suspect an exception or something is going wrong with the request.
Alternatively, you can try doing the request in the ResumeAfter<T> method you specified when calling the Form instead of that in the OnCompletion delegate

How to edit array to add keynames, from a httpclient api call in MVC?

I am very new to MVC and making api calls server side and need a little guidance. I have created a simple method to call an api to retrieve results in a JSON object:
apiController.cs (normal controller.cs file)
[HttpGet]
public JsonResult getDefaultStuff(string a = "abc") {
var url = "https://myapiurl";
var client = new HttpClient();
client.DefaultRequestHeaders.UserAgent.ParseAdd("Blah");
var response = client.GetStringAsync(url);
return Json(response, JsonRequestBehavior.AllowGet);
}
The results return in an array like this:
{Result: {examples: [[0000,6.121],[0000,1.122],[0000,9.172]]},"Id":81,"Exception":null,"Status":5,"IsCanceled":false,"IsCompleted":true,"CreationOptions":0,"AsyncState":null,"IsFaulted":false}
I need it to return with keynames like this :
{
"examples": [
{
"Keyname1": "45678",
"Keyname2": "1234"
},
{
"Keyname1": "14789",
"Keyname2": "1234"
},
{
"Keyname1": "12358",
"Keyname2": "4569"
}
]
}
Do I need to use IDictonary? I am unsure of the approach. Do I create a new object and then loop through each result adding keynames? an example would be much appreciated or just the approach will be very helpful.
You can do the following:
By using the Json.Net nuget package first deserialize the response into, for example, an anonymous object:
var deserialized = JsonConvert.DeserializeAnonymousType(response, new
{
examples = new[] { new decimal[] { } }
});
Then transform this object into the new one that has the property structure you need:
var result = new
{
examples = deserialized.Result.examples.Select(x => new
{
Keyname1 = x[0],
Keyname2 = x[1]
})
};
And return it to the client:
return Json(result, JsonRequestBehavior.AllowGet);
This is what your solution could roughly look like but you do have to keep several things in mind though:
Exception handling for deserialization
Checks for possible nulls in the deserialized object
Maybe also the safer way of retrieving values from the array to avoid possible out of range exceptions.
Also the GetStringAsync method is asynchronous and you should put an await keyword in front of it, but in order to do so you need to make your method async as well:
public async Task<JsonResult> getDefaultStuff(...)
If you don't have enough knowledge of asynchronous programming, here is the most advanced, in-depth and comprehensive video explaining it from top to bottom I have ever seen, so check it out whenever you find time...

Can't return HTTP status and data from a method after using httpClient.GetAsync()

I am having issues with the following code. I am retrieving a JSON object as a string and then wish to return this from my method so it can be used elsewhere. When I do however I get the message:
'filmsGlossary.searchQueries.returnJson(object); returns void, a return keyword must not be followed by an object expression'
public async void returnJson(object term)
{
//Set Variables
var searchFormat = "&format=json";
var termValue = term;
var httpClient = new HttpClient();
try
{
//Set web service URL format
string baseURI = "http://localhost/filmgloss/webService/web-service.php?termName=";
string userURI = baseURI + termValue + searchFormat;
//Send URL to web service and retrieve response code.
var response = await httpClient.GetAsync(userURI);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return content.ToString();
}
catch (HttpRequestException hre)
{
}
catch (Exception ex)
{
}
}
Originally I was only returning
return content;
However after reading it seemed that the issue might be that I needed to change this to:
return content.ToString();
However this did not help. I also read that I could change it to a synchronous, rather than asynchronous method and change the method to be 'public string' however I am only just learning c# and don't yet fully understand the implications of this (or how to do it).
Is it possible to resolve this error within the code I already have?
I would also like to understand the cause of the issue rather than just know the solution.
Thanks.
You really should paste the error messages that you are getting.
Why does your function declaration return void? It should return Task<string>.
public async Task<string> returnJson(object term)
Also in the body you should return the Task, like this:
await response.Content.ReadAsStringAsync();

Portable Class Library HttpClient

For one of my projects I want to develop a library that can be used in different platforms (Desktop, Mobile, Surface, etc). Hence have opted Porable Class Library.
I am developing a class for calling different API calls' using HttpClient. I am stuck with how to call the method, response and work around. This is my code :-
public static async Task<JObject> ExecuteGet(string uri)
{
using (HttpClient client = new HttpClient())
{
// TODO - Send HTTP requests
HttpRequestMessage reqMsg = new HttpRequestMessage(HttpMethod.Get, uri);
reqMsg.Headers.Add(apiIdTag, apiIdKey);
reqMsg.Headers.Add(apiSecretTag, ApiSecret);
reqMsg.Headers.Add("Content-Type", "text/json");
reqMsg.Headers.Add("Accept", "application/json");
//response = await client.SendAsync(reqMsg);
//return response;
//if (response.IsSuccessStatusCode)
//{
string content = await response.Content.ReadAsStringAsync();
return (JObject.Parse(content));
//}
}
}
// Perform AGENT LOGIN Process
public static bool agentStatus() {
bool loginSuccess = false;
try
{
API_Utility.ExecuteGet("http://api.mintchat.com/agent/autoonline").Wait();
// ACCESS Response, JObject ???
}
catch
{
}
finally
{
}
Like ExecuteGet, I will also create for ExecutePost. My query is from ExecuteGet, if (1) I pass JObject on parsing when IsSuccessStatusCode only, then how can I know about any other errors or messages to inform the user. (2) If I pass response, then how do I assign it here
response = API_Utility.ExecuteGet("http://api.mintchat.com/agent/autoonline").Wait();
that is giving error.
What would be the best approach to handle this situation ? And I got to call multiple API's, so different API will have different result sets.
Also, can you confirm that designing this way and adding PCL reference I will be able to access in multiple projects.
UPDATE :-
As mentioned in below 2 answers I have updated my code. As mentioned in the provided link I am calling the from the other project. This is my code :-
Portable Class Library :-
private static HttpRequestMessage getGetRequest(string url)
{
HttpRequestMessage reqMsg = new HttpRequestMessage(HttpMethod.Get, url);
reqMsg.Headers.Add(apiIdTag, apiIdKey);
reqMsg.Headers.Add(apiSecretTag, ApiSecret);
reqMsg.Headers.Add("Content-Type", "text/json");
reqMsg.Headers.Add("Accept", "application/json");
return reqMsg;
}
// Perform AGENT LOGIN Process
public static async Task<bool> agentStatus() {
bool loginSuccess = false;
HttpClient client = null;
HttpRequestMessage request = null;
try
{
client = new HttpClient();
request = getGetRequest("http://api.mintchat.com/agent/autoonline");
response = await client.SendAsync(request).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
JObject o = JObject.Parse(content);
bool stat = bool.Parse(o["status"].ToString());
///[MainAppDataObject sharedAppDataObject].authLogin.chatStatus = str;
o = null;
}
loginSuccess = true;
}
catch
{
}
finally
{
request = null;
client = null;
response = null;
}
return loginSuccess;
}
From the other WPF project, in a btn click event I am calling this as follows :-
private async void btnSignin_Click(object sender, RoutedEventArgs e)
{
/// Other code goes here
// ..........
agent = doLogin(emailid, encPswd);
if (agent != null)
{
//agent.OnlineStatus = getAgentStatus();
// Compile Error at this line
bool stat = await MintWinLib.Helpers.API_Utility.agentStatus();
...
I get these 4 errors :-
Error 1 Predefined type 'System.Runtime.CompilerServices.IAsyncStateMachine' is not defined or imported D:\...\MiveChat\CSC
Error 2 The type 'System.Threading.Tasks.Task`1<T0>' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Threading.Tasks, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f89d50a3a'. D:\...\Login Form.xaml.cs 97 21
Error 3 Cannot find all types required by the 'async' modifier. Are you targeting the wrong framework version, or missing a reference to an assembly? D:\...\Login Form.xaml.cs 97 33
Error 4 Cannot find all types required by the 'async' modifier. Are you targeting the wrong framework version, or missing a reference to an assembly? D:\...\Login Form.xaml.cs 47 28
I tried adding System.Threading.Tasks from the PCL library only, that gave 7 different errors. Where am I going wrong ? What to do to make this working ?
Please guide me on this. Have spend lots of hours figuring the best to develop a library accessible to desktop app & Win Phone app.
Any help is highly appreciative. Thanks.
If you call an async api when making the http calls, you should also expose that async endpoint to the user, and not block the request using Task.Wait.
Also, when creating a third party library, it is recommanded to use ConfigureAwait(false) to avoid deadlocks when the calling code tries to access the Result property or the Wait method. You should also follow guidelines and mark any async method with Async, so the method should be called ExecuteStatusAsync
public static Task<bool> AgentStatusAsync()
{
bool loginSuccess = false;
try
{
// awaiting the task will unwrap it and return the JObject
var jObject = await API_Utility.ExecuteGet("http://api.mintchat.com/agent/autoonline").ConfigureAwait(false);
}
catch
{
}
}
And inside ExecuteGet:
response = await client.SendAsync(reqMsg).ConfigureAwait(false);
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
In case IsSuccessStatusCode is false, you may throw an exception to the calling code to show something went wrong. To do that, you can use the HttpResponseMessage.EnsureSuccessStatusCode which throws an exception if the status code != 200 OK.
Personally, if ExecuteGet is a public API method i would definitely not expose it as a JObject but a strongly typed type.
If you want the result of the task, you need to use the Result property:
var obj = API_Utility.ExecuteGet("http://api.mintchat.com/agent/autoonline").Result;
However, it's usually not a good idea to wait synchronously for an async method to complete, because it can cause deadlocks. The better approach is to await the method:
var obj = await API_Utility.ExecuteGet("http://api.mintchat.com/agent/autoonline");
Note that you need to make the calling method async as well:
public static async Task<bool> agentStatus()
Sync and async code don't play together very well, so async tends to propagate across the whole code base.

Categories