My C# app uploads file to some API, I'm using multipart request, i.e I'm uploading a json string and binary contect of the file, it works fine for most files, but for very few it does nothing, I mean let's try for file named file.pdf:
My code looks roughly as follows:
public async Task<Dictionary<string , string>> Upload(string filePath)
{
FileInfo fi = new FileInfo(FilePath);
string jsonString="some json string";
byte[] fileContents=File.ReadAllBytes(fi.FullName);
Uri webService = new Uri(url);
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post , webService);
requestMessage.Method = HttpMethod.Post;
requestMessage.Headers.Add("Authorization" , "MyKey1234");
const string boundry = "------------------My-Boundary";
MultipartFormDataContent multiPartContent = new MultipartFormDataContent(boundry);
ByteArrayContent byteArrayContent = new ByteArrayContent(fileContents);
multiPartContent.Add(byteArrayContent);
requestMessage.Content = multiPartContent;
HttpClient httpClient = new HttpClient();
Console.WriteLine("before");
HttpResponseMessage httpResponse = await httpClient.SendAsync(requestMessage , HttpCompletionOption.ResponseContentRead , CancellationToken.None);
Console.WriteLine("after");
}
The caller:
myDictionary = await Upload(filePath);
Output:
before
Press any key to continue . . .
I mean there is no exception, nothing, what is this? a bug?
Edit
The structure of the console app is as follows:
class Program
{
static void Main(string[] args)
{
new MyClass().Start();
}
}
And inside MyClass:
public async void Start()
{
myDictionary = await Upload(filePath);
}
As explained in the comment section, your main method does not await the awaitable call and will not wait for the HttpClient instance to process the response.
If the console app is for testing purposes, you can call the .Result property on the task instance returned by the method, like this:
new MyClass().Start().Result;
However, it would be best to use the async keyword on the main method that has been made available in C# 7.1, like this:
class Program
{
static async Task Main(string[] args)
{
await new MyClass().Start();
}
}
Finally, as recommended, you should add the suffix 'Async' to your async method names. For instance, Start would be named StartAsync.
Related
My C# app uploads file to some API, I'm using multipart request, i.e I'm uploading a json string and binary contect of the file, it works fine for most files, but for very few it gives exception, I mean let's try for file named 50MB.zip
I'm getting the exception:
A task was canceled. ::: ::: System.Threading.Tasks.TaskCanceledException: A task was canceled.
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
My code looks roughly as follows:
public async Task<Dictionary<string , string>> Upload(string filePath)
{
FileInfo fi = new FileInfo(FilePath);
string jsonString="some json string";
byte[] fileContents=File.ReadAllBytes(fi.FullName);
Uri webService = new Uri(url);
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post , webService);
requestMessage.Method = HttpMethod.Post;
requestMessage.Headers.Add("Authorization" , "MyKey1234");
const string boundry = "------------------My-Boundary";
MultipartFormDataContent multiPartContent = new MultipartFormDataContent(boundry);
ByteArrayContent byteArrayContent = new ByteArrayContent(fileContents);
multiPartContent.Add(byteArrayContent);
requestMessage.Content = multiPartContent;
HttpClient httpClient = new HttpClient();
HttpResponseMessage httpResponse = await httpClient.SendAsync(requestMessage , HttpCompletionOption.ResponseContentRead , CancellationToken.None);
//exception in this line ^
return new Dictionary<string , string>();
}
The caller:
myDictionary = await Upload(filePath);
The structure of the console app is as follows:
class Program
{
static void Main(string[] args)
{
MainAsync().Wait();
}
static async Task MainAsync()
{
new MyClass().Start();
}
}
And inside MyClass:
public async void Start()
{
myDictionary = await Upload(filePath);
}
I guess I'm not using async correctly, can you maybe see what I'm missing? any ideas?
I'm 99% sure this error is due to a timeout, or the fact you don't actually await your Start method in MainAsync
I've addressed the timeout issue in the follow code along with some other small changes, which don't necessarily answer your question but hopefully help you nevertheless
class Program
{
private static HttpClient httpClient;
static void Main(string[] args)
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("your base url");
// add any default headers here also
httpClient.Timeout = new TimeSpan(0, 2, 0); // 2 minute timeout
MainAsync().Wait();
}
static async Task MainAsync()
{
await new MyClass(httpClient).StartAsync();
}
}
What I've done here is move the HttpClient out from your Upload() method because this class is designed to be reused many times. I've passed the httpClient object to MyClass's constructor which you'll see in the next code snippet.
I've also changed MainAsync() to await the StartAsync (renamed from Start to StartAsync because it's convention to suffix async methods) because in your original code MainAsync() wasn't actually awaiting anything
As I mentioned in the comments, you can change MainAsync().Wait() to await MainAsync() if you change Main to be static async Task Main, which will require you to change your build language to C# 7.1 or above
public class MyClass
{
private Dictionary<string, string> myDictionary;
private readonly HttpClient httpClient;
public MyClass(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public async Task StartAsync()
{
myDictionary = await UploadAsync("some file path");
}
public async Task<Dictionary<string, string>> UploadAsync(string filePath)
{
byte[] fileContents;
using (FileStream stream = File.Open(filePath, FileMode.Open))
{
fileContents = new byte[stream.Length];
await stream.ReadAsync(fileContents, 0, (int)stream.Length);
}
HttpRequestMessage requestMessage = new HttpRequestMessage();
// your request stuff here
HttpResponseMessage httpResponse = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, CancellationToken.None);
// parse response and return the dictionary
}
}
In MyClass I've made the following changes
Added a HttpClient parameter to the class' constructor, so we can pass our global HttpClient object to this class for it to reuse (which is what we do in MainAsync())
As mentioned before, I've renamed Start and Upload to StartAsync and UploadAsync because it's good practice to suffix async methods with Async
Start was changed from void to a Task because you should only use async void for event handlers
I changed the way your file is read to also be async, because it seems wasteful to have an async method that then blocks the CPU waiting for File.ReadAllBytes to finish. Wherever possible you should use async/await for I/O.
Currently working with the outlook api, even tough I usually work with the outlook library acquired via Nuget; I have reached a limitation where I am not able to accept event invitations. So I proceeded in making a a restful call out to the the outlook api. However, when I am making the call I am getting the following message {"error":{"code":"InvalidMethod","message":"An action can only be invoked as a 'POST' request."}} when executing the call.
Bad Code
class Program
{
static void Main(string[] args)
{
var testAccept = ExecuteClientCall.AcceptEvent().Result;
}
public static async Task<bool> AcceptEvent()
{
AuthenticationContext authenticationContext = new AuthenticationContext(CrmPrototype.Helpers.AuthHelper.devTenant);
try
{
var token = await GetTokenHelperAsync(authenticationContext, CrmPrototype.Helpers.AuthHelper.OutlookAuthenticationEndpoint);
string requestUrl = "https://outlook.office.com/api/v2.0/Users/***#nowwhere.com/events('AAQkAGZiNDQxZTVkLWQzZjEtNDdjNy04OTc4LTM4NmNjM2JiOTRjNAAQAFpV0CnWR0FIpWFYRtszPHU=')/accept";
HttpClient hc = new HttpClient();
hc.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
var method = new HttpMethod("POST");
var request = new HttpRequestMessage(method, requestUrl)
{
Content = new StringContent("{SendResponse: true}", Encoding.UTF8, "application/json")
};
HttpResponseMessage hrm = await hc.GetAsync(requestUrl);
if (hrm.IsSuccessStatusCode)
{
string jsonresult = await hrm.Content.ReadAsStringAsync();
var stophere = 0;
}
else
{
return false;
}
return true;
}
catch (Exception ex)
{
throw;
}
}
}
Maybe the reason is that you called
hc.GetAsync(requestUrl);
The doc said that this method:
Sends a GET request to the specified Uri as an asynchronous operation.
Try:
PostAsync(Uri, HttpContent)
https://msdn.microsoft.com/en-us/library/system.net.http.httpclient(v=vs.118).aspx
Hope this help you.
Your variable request contains an HttpRequestMessage object that you have created, but your code presently doesn't do anything with it.
Try replacing the line
HttpResponseMessage hrm = await hc.GetAsync(requestUrl);
(which, as pointed out by the other answer, makes a GET request), with
HttpResponseMessage hrm = await hc.SendAsync(request);
The following code just hangs when called from an ASP.NET app:
private async Task<XPathNavigator> UspsCreateAndPostRequest(string sUrl)
{
HttpClient client = new HttpClient();
byte[] urlContents = await client.GetByteArrayAsync(sUrl);
string sResponse = System.Text.Encoding.UTF8.GetString(urlContents);
... //more code to return XPathNavigator object based on response
}
If I change to following it works fine:
private async Task<XPathNavigator> UspsCreateAndPostRequest(string sUrl)
{
HttpClient client = new HttpClient();
byte[] urlContents = null;
var task = Task.Run(async () => { urlContents = await client.GetByteArrayAsync(strUrl); });
task.Wait();
string sResponse = System.Text.Encoding.UTF8.GetString(urlContents);
... //more code to return XPathNavigator object based on response
}
Is the fact that method signature return is a Task<XPathNavigator> causing the issue? Thank you.
Somewhere higher up the call stack there is a .Wait() being performed on the task that is returned from UspsCreateAndPostRequest.
Because you wrapped the call inside a Task.Run you lost the execution context, that is why it works. Doing
private async Task<XPathNavigator> UspsCreateAndPostRequest(string sUrl)
{
HttpClient client = new HttpClient();
byte[] urlContents = await client.GetByteArrayAsync(sUrl).ConfigureAwait(false);
string sResponse = System.Text.Encoding.UTF8.GetString(urlContents);
... //more code to return XPathNavigator object based on response
}
would achieve the same goal with less resources (but it would be even better to fix the wait higher up on the chain).
I have this async method:
public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
using (var client = new HttpClient())
{
HttpResponseMessage message = await client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json"));
var readAsStringAsync = await message.Content.ReadAsStringAsync();
return await readAsStringAsync.FromJsonAsync<RES>(mySerializerSettings);
}
}
Where FromJsonAsync is implemented as an extension method:
public async static Task<T> FromJsonAsync<T>(this string data, JsonSerializerSettings settings) where T : new()
{
return (T)(await JsonConvert.DeserializeObjectAsync<T>(data, settings));
}
Now I want to add a regular synchronous Post method and I thought the implementation would be:
public RES Post<RES>(string url, string content) where RES : new()
{
return PostAsync<RES>(url, content).Result;
}
But this doesn't really work. I see that the request is sent via a Http sniffer and I get a response back, but I get stuck when debugging and can't continue.
BTW, this does work (with Result instead of await):
public RES Post<RES>(string url, string content) where RES : new()
{
using (var client = new HttpClient())
{
HttpResponseMessage message = client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json")).Result;
var readAsStringAsync = message.Content.ReadAsStringAsync().Result;
return readAsStringAsync.FromJson<RES>(mySerializerSettings);
}
}
Where FromJson is implemented as an extension method:
public static T FromJson<T>(this string data, JsonSerializerSettings settings) where T : new()
{
return (T)JsonConvert.DeserializeObject<T>(data, settings);
}
The application is a web backend (WebApi).
What am I doing wrong?
You probably have a deadlock on your hands.
Asp.net uses a SynchronizationContext to post continuations back to the request context. If the context is blocked (like it is in your case on PostAsync<RES>(url, content).Result) then the continuation can't be executed and so the async method can't complete and you have a deadlock.
You can avoid it by using ConfigureAwait(false):
public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
using (var client = new HttpClient())
{
HttpResponseMessage message = await client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json"));
var readAsStringAsync = await message.Content.ReadAsStringAsync().ConfigureAwait(false);
return await readAsStringAsync.FromJsonAsync<RES>(mySerializerSettings).ConfigureAwait(false);
}
}
But it's better to just avoid blocking synchronously on async code to begin with and having two different versions for sync and async.
Although possible, I wouldn't use the answer provided by #i3arnon. Generally, you shouldn't block on async code. Although ConfigureAwait(false) does work, it can lead to confusion in your code-base where other developers may also end up blocking using .Result, without using ConfigureAwait or understanding the implications of that.
Instead, expose synchronous methods which are really synchronous:
public RES Post<RES>(string url, string content) where RES : new()
{
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/json";
var result = client.UploadString(url, content);
return JsonConvert.DeserializeObject<RES>(result, jsonSerializerSettings);
}
}
It seems you have a non-async function and you want to start a task that will call PostAsync and wait for this task to finish and return the result of the Task. Is this your problem?
To start a Task, use Task.Run( () => ...);
To wait for the Task use Task.Wait(...);
To see if the task stopped because of an exception: Task.IsFaulted
The result of the task is in Task.Result
Your code could be:
public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
// start the task that will call PostAsync:
var postTask = Task.Run( () => PostAsync(url, content));
// while this task is running you can do other things
// once you need the result: wait for the task to finish:
postTask.Wait();
// If needed check Task.IsFaulted / Task.IsCanceled etc. to check for errors
// the returned value is in Task.Result:
return postTask.Result;
}
I am new To C# programming.
I want to develop a simple Weather App using openweathermap API's.
I want to download and read contents of a file from an URL.
This is my Code to download file contents:
class WebClientToDownload
{
string webresponse;
public async void DownloadFile(string url)
{
string baseurl = "http://api.openweathermap.org/data/2.5/forecast/daily?q=";
StringBuilder sb = new StringBuilder(baseurl);
sb.Append(url + "&mode=json&units=metric&cnt=7");
string actual = sb.ToString();
HttpClient http = new System.Net.Http.HttpClient();
HttpResponseMessage response = await http.GetAsync(actual);
webresponse = await response.Content.ReadAsStringAsync();
}
public string StringReturn()
{
return webresponse;
}
string passed to the function is the name of city.
This is MainPage Code where I call those functions:
string JSONData;
private void GetWeatherButton_Click(object sender, RoutedEventArgs e)
{
WebClientToDownload Cls = new WebClientToDownload();
Cls.DownloadFile(GetWeatherText.Text);
JSONData = Cls.StringReturn();
JSONOutput.Text = JSONData;
}
I am getting an error at last line of code says as
An exception of type 'System.ArgumentNullException' occurred in mscorlib.dll but was not handled in user code
Additional information: Value cannot be null.
It looks like it was down to your use of await. Basically, await will pass control back to the calling function and allow it to continue until it is awaited, which isn't happening in your case so it is calling Cls.StringReturn() before the data has been returned. You can change as follows:
In your form:
string JSONData;
// Note the async keyword in the method declaration.
private async void GetWeatherButton_Click(object sender, EventArgs e)
{
WebClientToDownload Cls = new WebClientToDownload();
// Notice the await keyword here which pauses the execution of this method until the data is returned.
JSONData = await Cls.DownloadFile(GetWeatherText.Text);
JSONOutput.Text = JSONData;
}
In your download class:
class WebClientToDownload
{
// Notice this now returns a Task<string>. This will allow you to await on the data being returned.
public async Task<string> DownloadFile(string url)
{
string baseurl = "http://api.openweathermap.org/data/2.5/forecast/daily?q=";
StringBuilder sb = new StringBuilder(baseurl);
sb.Append(url + "&mode=json&units=metric&cnt=7");
string actual = sb.ToString();
HttpClient http = new System.Net.Http.HttpClient();
HttpResponseMessage response = await http.GetAsync(actual);
// Return the result rather than setting a variable.
return await response.Content.ReadAsStringAsync();
}
}
I've tested and it returned valid data but if any of this isn't clear please let me know.