HttpClient.PutAsync finish immediately with no response - c#

I try to upload a file with PUT method to the http server (Apache Tika) with the following code
private static async Task<string> send(string fileName, string url)
{
using (var fileStream = File.OpenRead(fileName))
{
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
var content = new StreamContent(fileStream);
content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
var response = await client.PutAsync(url, content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
In Main the method is called this way:
private static void Main(string[] args)
{
// ...
send(options.FileName, options.Url).
ContinueWith(task => Console.WriteLine(task.Result));
}
In response the server should return HTTP 200 and text response (parsed pdf file). I've checked this behavior with with Fiddler and it works fine as far as the server is concerned.
Unfortunately the execution finish right after calling PutAsync method.
What I do wrong?

You're executing this from a console application, which will terminate after your call to send. You'll have to use Wait or Result on it in order for Main not to terminate:
private static void Main(string[] args)
{
var sendResult = send(options.FileName, options.Url).Result;
Console.WriteLine(sendResult);
}
Note - this should be only used inside a console application. Using Task.Wait or Task.Result will result in a deadlock in other application types (which are not console) due to synchronization context marshaling.

Related

UI thread waits for "await client.SendAsync(request).ConfigureAwait(false)" to finish before moving to another url

Client starts some action via button. In controller its async method: async public Task<JsonResult> CreatePresentation that contains line using (var response = await client.SendAsync(request).ConfigureAwait(false)). Lets say this line takes 10s to finish. While that is happenning client wants to go to another url inside website and clicks on that url (that view is also async meaning its async public Task<ActionResult> BRDetails(int id)). But asp .net does not serve him view immidiatly. Instead it awaits for await line to finish before serving him the view (10s).
Why does asp framework waits for await line to finish before it starts producing view for next url that client requested?
I would thought that deliving the next url should start immidiatly in second thread and waits in first for await to finishes because of .ConfigureAwait(false).
Environment:
.net framework v4.5,
iiexpress that comes with visual studio
Here is part of code (since it's a software product I am not able to share full code):
async public Task<JsonResult> CreateReportFile(int id, string email)
{
try
{
var fileName = "Report " + DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss") + ".txt";
fileName = fileName.Replace('/', '_');
fileName = fileName.Replace(':', '_');
var fullPath = "some path";
var presentationTask = new Creator().CreateSomeFile("some values to pass", fileName);
var ms = await presentationTask.ConfigureAwait(false);
using (FileStream file = new FileStream(fullPath, FileMode.Create, System.IO.FileAccess.Write))
{
ms.WriteTo(file);
ms.Close();
}
var emailAttachment = BusinessReportsEmail.savePresentation(fullPath, fileName);
await BusinessReportsEmail.sendEmail(emailAttachment, email).ConfigureAwait(false);
return Json(new
{
Status = "ok",
Data = email
});
}
catch (Exception ex)
{
return Json(new
{
Status = "error",
Data = ex.Message
});
}
}
public async Task<MemoryStream> CreateSomeFile(string programData, string outputFileName)
{
// Use this in production
var url = string.Format(System.Configuration.ConfigurationManager.AppSettings["workFileUrl"]);
var appCode = string.Format(System.Configuration.ConfigurationManager.AppSettings["appCode"]);
var appKey = string.Format(System.Configuration.ConfigurationManager.AppSettings["appKey"]);
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMinutes(20);
using (var request = new HttpRequestMessage(HttpMethod.Post, url))
{
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Add("AppCode", appCode);
request.Headers.Add("AppKey", appKey);
var jsonString = JsonConvert.SerializeObject(new
{
InputType = "json",
Content = programData
});
request.Content = new StringContent(jsonString, Encoding.UTF8, "application/json");
using (var response = await client.SendAsync(request).ConfigureAwait(false))
{
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception($"Failed to create {outputFileName} file.");
var reponseObject = await response.Content.ReadAsAsync<FileResponse>().ConfigureAwait(false);
return new MemoryStream(Convert.FromBase64String(reponseObject.Content));
}
}
}
}
and than client goes to this view but needs to wait for previous call to finish.
async public Task<ActionResult> ReviewDetails(int id)
{
return View();
}
Why does asp framework waits for await line to finish before it starts producing view for next url that client requested?
In ASP.NET, await yields to the thread pool, not the HTTP client.
Remember, your web app is serving HTTP responses to HTTP requests. Generally speaking, a single HTTP request has a single HTTP response. Your web app can't return a response and then sometime later return another response. You can think of an await as an "asynchronous wait", and your method will asynchronously wait there, so it won't even get to the return Json(...) until after the await completes.
If you want to kick off an independent background process, then I recommend using asynchronous messaging, as described on my blog. It's complex, but it's necessary for reliable work. Alternatively, you could use something like a SPA and not return early at all - allow the user to change pages while the HTTP request is still in progress.

App goes into Break Mode when I try to use Json with HttpClient

I am trying to post data from my app from a register page to a web service (testing with requestb.in) however when I try to use the below code it puts the app into break mode, then when I use break points to find where the problem is it just shows that "await PostRequest(...)" is causing the problem.
I have installed System.Net.Http on both the Portable project and the android project.
public async Task<JObject> PostAsync(string uri, string data)
{
var httpClient = new HttpClient();
var response = await httpClient.PostAsync(uri, new StringContent(data));
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
return await Task.Run(() => JObject.Parse(content));
}
Then for the button clicked where this method is called:
async void NextBtnClicked(object sender, EventArgs)
{
await PostAsync("https://requestb.in/MYURL", Username.Text);
}
Username.Text is the string from an entry field in the XAML class, This will recreate my problem
Here is the generic method I made to post data to an API.
As #maccettura pointed out above, it is best practice to reuse HttpClient and I've included that in the code, below.
HttpClient Post
static readonly Lazy<HttpClient> _clientHolder = new Lazy<HttpClient>(() => CreateHttpClient(TimeSpan.FromSeconds(60)));
static HttpClient Client => _clientHolder.Value;
protected static async Task<HttpResponseMessage> PostObjectToAPI<T>(string apiUrl, T data)
{
var stringPayload = await Task.Run(() => JsonConvert.SerializeObject(data)).ConfigureAwait(false);
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
try
{
return await Client.PostAsync(apiUrl, httpContent).ConfigureAwait(false);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
return null;
}
}
static HttpClient CreateHttpClient(TimeSpan timeout)
{
var client = new HttpClient();
client.Timeout = timeout;
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
return client;
}
This snippet was taken from my BaseHttpClientService.cs that I copy/paste into any app that needs to use HttpClient:
https://github.com/brminnick/XamList/blob/master/XamList/Services/BaseHttpClientService.cs
Troubleshoot your application using UWP so that you can see what kind of exception it is throwing. Try also to downgrade the two Nuget packages.

Xamarin Android HttpClient PostAsync

I have an android app in Xamarin native. I am trying to consume a Restful service from an API in another server.
I have this:
private async Task<string> CreateCellphone(string url, Cellphone cell)
{
string cellphone = JsonConvert.SerializeObject(cell);
HttpContent content = new StringContent(cellphone, Encoding.UTF8, "application/json");
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.PostAsync(url, content);
string responseMessage = await response.Content.ReadAsStringAsync();
return responseMessage;
}
}
I execute this on a button call like this:
private void RegisterButtonOnClick(object sender, EventArgs e)
{
// Create new GUID
Guid obj = Guid.NewGuid();
// Store the created GUID in a private shared preferences file
var localGUID = Application.Context.GetSharedPreferences("LocalSetup", FileCreationMode.Private);
var guidEdit = localGUID.Edit();
guidEdit.PutString("GUID", obj.ToString());
guidEdit.PutBoolean("IsRegistered", true);
guidEdit.Commit();
// Create the cellphone record into the database for DB admin to activate
_url = Resources.GetString(Resource.String.cellphone_api_url);
Cellphone cell = new Cellphone();
cell.CellphoneId = obj.ToString();
var response = CreateCellphone(_url, cell);
}
But when my code gets to the postAsync method, nothing happens, it just continues without actually sending the code to the endpoint, I have no idea what I might be doing wrong, because all documentation I have on PostAsync tells me this is how to send json data for a Restful Web api endpoint.
Thank you in advance for any pointers.
You need to await the call to CreateCellphone or nothing will happen because the response Task will get disposed of almost immediately. Not sure if you can make your button click method async in Xamarin, but I would try this:
private async void RegisterButtonOnClick(object sender, EventArgs e)
//^^^^^
//Add this
{
//snip
await CreateCellphone(_url, cell);
}
Failing that, there are various way to call an async method synchronously, check this question.

How to post data using HttpClient? (an answer than actually works)

There is another question about this, but it doesn't have a functioning solution at the end, and the only good answer, for some reason, doesn't work, not for the guy who ask it, not for me either.
This such question is here:
How to post data using HttpClient?
Given the corresponding aclarations, this is the code I have so far:
The methods to call the method who connects with the web server:
private void Button_Click(object sender, System.EventArgs e)
{
//. . . DO SOMETHING . . .
PopulateListView();
//. . . DO SOMETHING ELSE . . .
}
private void PopulateListView()
{
//. . . DO SOMETHING . . .
list = await "http://web.server.url".GetRequest<List<User>>();
//. . . DO SOMETHING ELSE . . .
}
The method than connects with the web server:
public async static Task<T> SendGetRequest<T>(this string url)
{
try
{
var uri = new Uri(url);
HttpClient client = new HttpClient();
//Preparing to have something to read
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("OperationType", "eaf7d94356e7fd39935547f6f15e1c4c234245e4")
});
HttpResponseMessage response = await client.PostAsync(uri, formContent);
#region - - Envio anterior (NO FUNCIONO, SIN USO) - -
//var stringContent = new StringContent("markString");
//var sending = await client.PostAsync(url, stringContent);
//MainActivity.ConsoleData = await client.PostAsync(url, stringContent);
#endregion
//Reading data
//var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
MainActivity.ConsoleData = json.ToString();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
}
catch(Exception ex)
{
Console.WriteLine("Error: "+ex.ToString());
return default(T);
}
}
You maybe guessed it, but I'm trying to make a method that send some data (through POST) called "markString" to a web-server than receive it and, depending of the "markString" it returns certain json Objects.
This web server is already working properly (I tested it out with some plug-in, it work like it should)
This method is supposed to send the "markString" and receive the data back so then i can use it in the app.
I'm making a Xamarin Android application.
Also have in mind than I don't have any connection problem at all, in fact the app is sending data in an excellent matter when I try to do it using web client, but I want it to send it using HttpClient.
The problem
The code is not returning anything. Any request for information, clarification, question, constructive comments or anything than can lead to an answer would be greatly appreciated too.
Thanks in advance.
Most deadlock scenarios with asynchronous code are due to blocking further up the call stack.
By default await captures a "context" (in this case, a UI context), and resumes executing in that context. So, if you call an async method and the block on the task (e.g., GetAwaiter().GetResult(), Wait(), or Result), then the UI thread is blocked, which prevents the async method from resuming and completing.
void Main()
{
var test = SendGetRequest("http://www.google.com");
test.Dump();
}
public async static Task<string> SendGetRequest(string url)
{
try
{
var uri = new Uri(url);
HttpClient client = new HttpClient();
//Preparing to have something to read
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("OperationType", "eaf7d94356e7fd39935547f6f15e1c4c234245e4")
});
HttpResponseMessage response = await client.PostAsync(uri, formContent);
#region - - Envio anterior (NO FUNCIONO, SIN USO) - -
//var stringContent = new StringContent("markString");
//var sending = await client.PostAsync(url, stringContent);
//MainActivity.ConsoleData = await client.PostAsync(url, stringContent);
#endregion
//Reading data
//var response = await client.GetAsync(url);
var json = await response.Content.ReadAsStringAsync();
return json;
}
catch (System.Exception ex)
{
Console.WriteLine("Error: " + ex.ToString());
return string.Empty;
}
}

HttpClient.PostAsync terminates program with exit code 0

Have look around the web and not found any answers.. I did find a post here with my same problem but it does not resolve my issue.
(HttpClient.PostAsync knocks out the app with exit code 0)
When I run this code, the post to vendorAddress works.
but when I get to post PaymentTerms the program terminates on the postAsync function with no error message, code or anything.
I don't understand why it works for one but not the other..
I have taken the same Url and json text and done a post thru chrome using the postman plugin. Both calls work and I can get results back.
I have changed my post to use WebClient and both call work and I get results.
but I need to keep HTTPClient service in my code.
Any suggestions?
static void Main(string[] args)
{
RunAsync().Wait();
}
static async Task RunAsync()
{
try
{
// works
var result = await enconPostData("vendorAddress", JsonVendorAdd);
// does not work. fails on postAsync
var result1 = enconPostData("PaymentTerms", null);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
static public async Task<string> enconPostData(string datatype, Object[] jsObject)
{
////jsObject is a json string/object////
string jsonString = null, URIaddress = null;
switch (datatype)
{
case "vendorAddress":
// Create Json Object to post
//EnVendors enconvend = new EnVendors();
EnVendors envend = new EnVendors();
envend.data = (Vendor[])jsObject;
URIaddress = baseUrl + "api/CONTACTS/UpdateXXXXXX";
// Serialize to a JsonString
jsonString = JsonConvert.SerializeObject(enconvend);
break;
case "PaymentTerms":
ContractInput entermdate = new ContractInput();
//Set JsonObject here with dates
entermdate.DateFrom = new DateTime(2016, 10, 1);
entermdate.DateTo = new DateTime(2016, 10, 30);
URIaddress = baseUrl + "api/PaymentTerms/ActiveXXXXXX";
// Serialize to a JsonString
jsonString = JsonConvert.SerializeObject(entermdate);
break;
}
return await PostAsync(URIaddress, jsonString);
}
static public async Task<string> PostAsync(string uri, string jsonString)
{
// Post to API Call
using (var Client = new HttpClient())
{
/////////
/// program aborts here at PostAsync on PaymentTerms Call. works fine for vendorAddress
////////
var response = await Client.PostAsync(uri, new StringContent(jsonString, Encoding.UTF8, "application/json"));
//will throw an exception if not successful
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
return await Task.Run(() => content);
}
}
Well, I have figured out my issue on reviewing my post here.
I had a break point set, so the red color of the break point made it hard to see my problem.
on line 22 of my example
var result1 = enconPostData("PaymentTerms", null);
is missing the await command
var result1 = await enconPostData("PaymentTerms", null);
once I added that.. I get my results, and the program did not terminate.
synchronous call vs asynchronous call
Thanks all.. just needed a new perspective i guess.

Categories