I have this line of code
var response = new HttpClient().PostAsJsonAsync(posturi, model).Result;
The Called WebAPI controller returns a bool to make sure the object was saved, but how do I return that bool response?
Continue to get from content:
var httpClient = new HttpClient();
var response = httpClient.PostAsJsonAsync(posturi, model).Result;
bool returnValue = response.Content.ReadAsAsync<bool>().Result;
But, this is really naive approach for quick way to get result. PostAsJsonAsync and ReadAsAsync is not designed to do like this, they are designed to support async await programming, so your code should be:
var httpClient = new HttpClient();
var response = await httpClient.PostAsJsonAsync(posturi, model);
bool returnValue = await response.Content.ReadAsAsync<bool>();
Also, instead of using a flag to check whether an object is saved or not, you should make use of HTTP codes by returning 200 OK to determine that saving is successfully.
The accepted answer is technically correct but blocks the current thread on calls to .Result. If you are using .NET 4.5 or higher, you should avoid that in almost all situations. Instead, use the equivalent asynchronous (non-blocking) version:
var httpClient = new HttpClient();
var response = await httpClient.PostAsJsonAsync(posturi, model);
bool returnValue = await response.Content.ReadAsAsync<bool>();
Note that the method containing the above code needs to be marked async, and should itself be awaited.
Since its an Async operation don't immediately do .Result as its wrong
Instead you need to do it async by doing this:
var httpClient = new HttpClient()
var task = httpClient.PostAsJsonAsync(posturi, model)
.ContinueWith( x => x.Result.Content.ReadAsAsync<bool>().Result);
// 1. GETTING RESPONSE - NOT ASYNC WAY
task.Wait(); //THIS WILL HOLD THE THREAD AND IT WON'T BE ASYNC ANYMORE!
bool response = task.Result
// 2. GETTING RESPONSE - TASK ASYNC WAY (usually used in < .NET 4.5
task.ContinueWith( x => {
bool response = x.Result
});
// 3. GETTING RESPONSE - TASK ASYNC WAY (usually used in >= .NET 4.5
bool response = await task;
NOTE: I just wrote them in here, so I didnt actually test them but more or less that's what you want.
I hope it helps!
I used HttpStatusCode to check the result.
public HttpStatusCode PostStaffPositions(Foo foo)
{
string uri = myapiuri;
using (HttpClient httpClient = new HttpClient())
{
var response = httpClient.PostAsJsonAsync(uri, foo).Result;
return response.StatusCode;
}
}
And then in Controller check it like this:
HttpStatusCode update = staffrest.PostStaffPositions(foo);
if (update == HttpStatusCode.OK)
{
//Update Succeed
}
else
{
//Update Failed
}
If you call the generic version, it should give you back the bool:
var response = new HttpClient().PostAsJsonAsync<bool>(posturi, model).Result;
At least according to the docs.
It's July 2021 and I'm using .net 5 (namely the .net core 5).
I did not see any generic methods above in System.Net.Http. Now the code looks like this (tested):
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://localhost:44330/api/Book/Add");
var response = client.PostAsJsonAsync(client.BaseAddress,
JsonSerializer.Serialize(_teamSummaries));
MessageBox.Show(#"Result is " + JsonSerializer.Serialize(response));
var returnValue = response.Result.Content.ReadAsStringAsync();
MessageBox.Show(#"Return value is " + returnValue.Result);
}
There are also ReadAsStringAsync, ReadAsByteArrayAsync, ReadAsStream, ReadFromJsonAsync, ReadFromJsonAsync<T> (this method returns Task<T>).
But from the text meaning "ReadFromJsonAsync", I think the T is not the bool mentioned above, but a class that contains the bool member. If you want to return something like book, give it a try.
On the other hands, since code on the server looks like this(.net 5):
[HttpPost]
[Route("Add")]
public async Task<ActionResult<IEnumerable<Book>>> Add(string value)
{
var all = await _dbCollection.FindAsync(Builders<Book>.Filter.Empty);
return Ok("Everything is ok.");
}
So, if we want to return true by bool, we should return Ok(...). If we want to return false by bool, we should return something else. There are more than 20 other types of results, which contains much more information rather than just "false".
Related
I'm successfully getting a token back from my GetAccessToken() and GetAccessTokenAsync methods, but the token isn't retrieved until after the main method of GetCourses, which won't work because that's the method that collects the data I need to show on my cshtml page. I've tried pulling apart this controller and creating a Globals class that will house just the URIs, apiKey, and token, but then I read that's bad practice for MVC so I ditched that effort. It was getting called after the GetCourses method anyway, so it was dead end too.
I'm newer to MVC and come from a WebForms background where I was used to being able to throw this kind of code in my PageInit, but am struggling to figure out how to pull this off in MVC. Can someone help me figure out what I am doing wrong or if I need to go about this a different way?
public ActionResult GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = GetAccessToken().ToString();
IEnumerable<Course> courses = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri((string)TempData["EthosURI"]);
client.DefaultRequestHeaders.Add("Authorization", "Bearer {" + (string)TempData["Token"] + "}");
client.DefaultRequestHeaders.Add("Accept", "application/json");
//HTTP GET
var responseTask = client.GetAsync("courses");
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
}
else //web api sent error response
{
//log response status here..
courses = Enumerable.Empty<Course>();
ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
}
}
return View(courses);
}
public static async Task<string> GetAccessToken()
{
var token = await GetAccessTokenAsync("redactedUrl", "redactedAPIKey");
return token;
}
public static async Task<string> GetAccessTokenAsync(string ethosURI, string apiKey)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ethosURI);
client.DefaultRequestHeaders.Accept.Clear();
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(ethosURI)
};
request.Headers.Clear();
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("Accept", "application/json");
request.Headers.CacheControl = new CacheControlHeaderValue() { NoCache = true };
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
The (non-blocking) way in C# to wait for a task to complete is to use the await keyword. And for a method to use the await keyword, it has to be marked async. By using await, you not only wait for the task to complete, but also the current thread is not blocked. Wrapping an asynchronous operation in another method would not make it synchronous. In other words, the asynchronous nature propagates up the call hierarchy and the caller has to await. So, the GetAccessToken() still has to be awaited. A controller action can be marked asynchronous as well, so you probably want:
public async Task<ActionResult> GetCourses()
{
TempData["EthosURI"] = "redacted";
TempData["Token"] = (await GetAccessToken()).ToString(); // note the additional parentheses
....
Note the additional parantheses above before calling ToString(). However, since GetAccessToken() already returns a string, you don't need the redundant ToString() call:
TempData["Token"] = await GetAccessToken();
Now, you can also change this:
var readTask = result.Content.ReadAsAsync<IList<Course>>();
readTask.Wait();
courses = readTask.Result;
to just:
courses = await result.Content.ReadAsAsync<IList<Course>>();
Microsoft has quite good documentation on asynchronous programming and I would recommend checking it out.
That's not how async works in C#. You need either to make GetCourses() async AND await for GetAccessToken(), or use dirty hack GetAccessToken().GetAwaiter().GetResult() but it may become not safe in certain circumstances.
I'm making a basic POST to receive an auth Token from an API.
My code starts the Task from a button press.
Task<DataClass.AuthenticationToken> token = GetAuthTokenAsync(tbUsername.Text.Trim(), tbPassword.Text.Trim());
The task should return my token information in a data structure defined in "dataclass"
private async Task<DataClass.AuthenticationToken> GetAuthTokenAsync(string username, string password)
{
string requestURL = DataClass.CloudAPIBaseURL + DataClass.CloudTokenURL;
HttpClient client = new HttpClient();
HttpResponseMessage response = new HttpResponseMessage();
DataClass.AuthenticationRequest request = new DataClass.AuthenticationRequest
{
username = username,
password = password
};
DataClass.AuthenticationToken token = new DataClass.AuthenticationToken();
// Post request
response = await client.PostAsJsonAsync(requestURL, request);
//never gets here!!!
// I will complete coding once I get the response.
//string statusCode = response.StatusCode.ToString();
return token;
}
Using Fiddler, I see my POST going out and the response containing the correct authorizing data.
For some reason, the async await is not returning.
The issue could be that the call to GetAuthTokenAsync isn't being awaited:
Task<DataClass.AuthenticationToken> token = GetAuthTokenAsync(tbUsername.Text.Trim(), tbPassword.Text.Trim());
Try this:
DataClass.AuthenticationToken token = await GetAuthTokenAsync(tbUsername.Text.Trim(), tbPassword.Text.Trim());
You can try first to check whether client.PostAsJsonAsync(requestURL, request) will return a value called synchronously i.e without awaiting the call:
response = client.PostAsJsonAsync(requestURL, request).Result;
If it returns a value maybe the problem is in the surrounding code that calls GetAuthTokenAsync i.e. it does not wait for the Task to complete or similar issue.
Trying the last suggestion:
DataClass.AuthenticationToken token = await GetAuthTokenAsync(tbUsername.Text.Trim(), tbPassword.Text.Trim());
the compiler does not allow this as a call to an async Task. The format I used to invoke the Task is correct. Continued experimenting for 5 days, I landed on NOT using the TASK await method. Instead I changed the Task to a function GetAuthToken(parameters).
Using the PostAsJsonAsync without await and adding .Result, the method awaits for a result.
HttpResponseMessage response = client.PostAsJsonAsync(requestURL, request).Result;
if (response.IsSuccessStatusCode)
{
// Ok
string data = response.Content.ReadAsStringAsync().Result;
thisToken = Newtonsoft.Json.JsonConvert.DeserializeObject<DataClass.AuthenticationToken>(data);
}
I used Newtonsoft to deserialize the data and voila. I can continue with my project!
Note to all those who tried to help. Thank you. It would be helpful however, if you first test your suggestions as some were just not valid coding syntax.
I am trying to use Microsoft Project OData by querying data in C#. I am having performances issues with delays around 1s for each query. I am trying to query 2 information at once using that method :
public static async Task<string> ReadXml(string url)
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = Credentials; // SharePointOnlineCredentials
request.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
using (var response = (HttpWebResponse)await request.GetResponseAsync())
using (var stream = response.GetResponseStream())
using (var reader = new System.IO.StreamReader(stream))
{
var xml = await reader.ReadToEndAsync();
return xml;
}
}
It works fine if I call it and always wait for it to end before calling it again, but I never receive any response from the WebRequest if I call it multiple times at once :
// just an example. I usually put a condition to filter for the tasks of a single project
var query1 = ReadXml(#"https://something.sharepoint.com/sites/pwa/_api/ProjectData/Projects");
var query2 = ReadXml(#"https://something.sharepoint.com/sites/pwa/_api/ProjectData/Tasks");
Task.WaitAll(query1, query2);
If I "await" the first one and then do the second one it works fine, but not with the code above. And this is assuming there is < 300 tasks in the project, if more than that I have to query them in chunk of 300 leading to 4 or 5 seconds for the entire query since I can't to them all at once!
Is there a way to send multiple request at the same time ?
I am able to do it by simply entering the url in multiple chrome tabs really fast / have faster responses. I don't understand why it doesn't work with my code!
Thanks,
According to the following post Webrequest.Create could be the problem, it uses an internally blocking method C# Thread UI is getting blocked | Possible reason WebRequest.Create?.
The code below uses the newer HttpClient and shouldn't have this issue.
public static HttpClient _HttpClient { get; } = new HttpClient(new HttpClientHandler { Credentials=new NetworkCredential("","")});
public static async Task<string> ReadXml(string url)
{
using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, url))
{
requestMessage.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
using (var response = await _HttpClient.SendAsync(requestMessage))
{
return await response.Content.ReadAsStringAsync();
}
}
}
I am calling a method of which returns Task, in the calling method I need to read the response in form of string.
Here's the code that I have put:
static public async Task<HttpResponseMessage> Validate(string baseUri,HttpContent content) {
HttpResponseMessage response = new HttpResponseMessage();
response = await client.PostAsync(baseUri,content);
return response;
}
public string test(){
string postJson = "{Login = \"user\", Password = \"pwd\"}";
HttpContent stringContent = new StringContent(postJson,
UnicodeEncoding.UTF8, "application/json");
HttpResponseMessage result=Validate(Uri,stringContent);
var json = result.Content.ReadAsStringAsync().Result;
}
I expect string but this error is thrown:
Cannot implicitly convert type
'System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>' to
'System.Net.Http.HttpResponseMessage'
Your problem is at:
HttpResponseMessage result=Validate(Uri,stringContent);
Validate is returning a task, not a HttpResponseMessage.
Change that to:
var result = Validate(Uri,stringContent).Result;
Just to clean up the code a bit and avoid using Result if you can afford to change it like:
static public async Task<HttpResponseMessage> Validate(string baseUri, HttpContent content)
{
return await client.PostAsync(baseUri,content);
}
public async Task<string> test()
{
var postJson = "{Login = \"user\", Password = \"pwd\"}";
var stringContent = new StringContent(postJson, UnicodeEncoding.UTF8, "application/json");
var result = await Validate(Uri,stringContent);
return await result.Content.ReadAsStringAsync();
}
Be consistent with your coding style. As to why calling .Result is bad, as people are pointing out in the comments, check this blog.
Method Validate returns the task and is asynchronous. Basically you can call this method in two different ways:
1) await the result of Validate in another async method, like this...
public async Task<string> test() {
string postJson = "{Login = \"user\", Password = \"pwd\"}";
HttpContent stringContent = new StringContent(postJson,
UnicodeEncoding.UTF8, "application/json");
HttpResponseMessage result = await Validate(Uri,stringContent);
var json = result.Content.ReadAsStringAsync().Result;
}
2) block current execution while waiting for this resul, like this...
public string test() {
string postJson = "{Login = \"user\", Password = \"pwd\"}";
HttpContent stringContent = new StringContent(postJson,
UnicodeEncoding.UTF8, "application/json");
HttpResponseMessage result = Validate(Uri,stringContent).Result;
var json = result.Content.ReadAsStringAsync().Result;
}
Choose the 1st way by default unless you really have an urge to block the calling thread.
Note that when using 2nd solution in WinForms, WPF or ASP.NET apps you will probably have a deadlock. modifying Validate method like this should solve the problem:
response = await client.PostAsync(baseUri,content).ConfigureAwait(false);
Just for readability change the method name from Validate(..) to ValidateAsync(..). Just to make clear that this method returns a task and you have to await it.
You're missing an await when calling your Validate method. If you do not want or can make the caller async then use the method GetAwaiter().GetResult() and you'll get what you want.
Replace ReadAsStringAsync().Result with ReadAsStringAsync().GetAwaiter().GetResult()
Like others wrote in comments calling .Result is bad because it could cause locks. The same applies in general to GetAwaiter().GetResult() but calling them is preferred over calling Result. The best option is to use await/async.
When you want to know whats the difference between GetAwaiter().GetResult() vs calling Result you can read this:
Is Task.Result the same as .GetAwaiter.GetResult()?
You can easily write:
var json = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
I have a method that is shared between two other methods which do the same thing, both post events to outlook calendar but in one case I don't need the response string returned, here is the method:
private static async Task<string> InitPost(Kalender kalender, string email)
{
using (Client)
{
var postUri = $"https://graph.microsoft.com/v1.0/users/{email}/calendar/events";
var httpContent = new StringContent(JsonConvert.SerializeObject(kalender), Encoding.UTF8, "application/json");
var method = new HttpMethod("POST");
var request = new HttpRequestMessage(method, postUri)
{
Content = httpContent
};
var response = await Client.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
}
Is it possible to call the method like so: await InitPost(KalenderEntity, email); or does it have to be var e = await InitPost(KalenderEntity, email);?
if possible, can someone explain what happens with the return string? I am new to coding and what I have learned in school is that if you have a method with a return type then you have to "catch" it.
No, you don't need to "catch" the returned value, it's fine to ignore the return value of the method. But, in some cases this might be a warning sign of a bad design that is, if it's possible that the two callers, are using the same method for two different things, you should split your method into two different implementation.
you can leave await InitPost(KalenderEntity, email); if you don't need a result, it's ok. In this case, you don't assign the result of this method to any variable and it will be removed from memory by GAC.