Could someone please help me modify the code below:
client.ExecuteAsync(request, response => {
Console.WriteLine(response.Content);
});
Basically I want to use ExecuteAsync method above but don't want to print but return response.Content to the caller.
Is there any easy way to achieve this?
I tried this but doesnt' work:
public T Execute<T>(RestRequest request) where T : new()
{
var client = new RestClient();
client.BaseUrl = BaseUrl;
client.Authenticator = new HttpBasicAuthenticator(_accountSid, _secretKey);
request.AddParameter("AccountSid", _accountSid, ParameterType.UrlSegment); // used on every request
var response = client.ExecuteAsync(request, response => {
return response.data);
});
}
The above code is from
https://github.com/restsharp/RestSharp
There's the thing... you can't return an asynchronously delivered value, because your calling method will already have returned. Blocking the caller until you have a result defeats the point of using ExecuteAsync. In this case, I'd return a Task<string> (assuming response.Content is a string):
Task<string> GetResponseContentAsync(...)
{
var tcs=new TaskCompletionSource<string>();
client.ExecuteAsync(request, response => {
tcs.SetResult(response.Content);
});
return tcs.Task;
}
Now, when the task completes, you have a value. As we move to c#5 async/await, you should get used to stating asynchrony in terms of Task<T> as it's pretty core.
http://msdn.microsoft.com/en-us/library/dd537609.aspx
http://msdn.microsoft.com/en-us/library/hh191443.aspx
With the help of #spender, this is what i got:
You can add new file in RestSharp project, and add this code:
public partial class RestClient
{
public Task<IRestResponse<T>> ExecuteAsync<T>(IRestRequest request)
{
var tcs=new TaskCompletionSource<IRestResponse<T>>();
this.ExecuteAsync(request, response =>
{
tcs.SetResult(
Deserialize<T>(request, response)
);
});
return tcs.Task;
}
}
This will practically return the full response, with status code and everything, so you can check if the status of the response is OK before getting the content, and you can get the content with:
response.Content
From reading the code it looks like you want to use ExecuteAsGet or ExecuteAsPost instead of the async implementation.
Or maybe just Execute- not sure exactly what type Client is.
At some point, there was a bunch of overloads introduced that support System.Threading.Tasks.Task and had the word "Task" in them. They can be awaited (like HttpClient's methods).
For instance:
ExecuteTaskAsync(request) (returns Task<IRestResponse>)
ExecuteGetTaskAsync(request) (returns Task<IRestResponse>)
ExecuteGetTaskAsync<T>(request) (returns Task<IRestResponse<T>>)
Those then became deprecated in later v106 versions in favour of Task-support being the default I believe, e.g. the first one became client.ExecuteAsync(request).
So these days you can just do:
var response = await client.ExecuteAsync(request);
return response.Content;
Related
Does anyone know why HttpClient - PostAsync doesn’t return. It just does nothing. I have had it working occasionally especially for one off posts but it seems sometimes to not do anything especially under load and it doesn't throw an exception which of course makes my code unreliable and hard to debug.
I have tried adding ConfigureAwait(false) It makes not difference.
I suspect the Task is failing to 'pack'
This is in a core 3.0 console app run on macOS Catalina using visual studio code
This code is pretty much copied from Microsoft's documentation and I’m calling Microsoft Graph when posting.
public static async Task PostAsync(HttpClient httpClient, string url, string token, HttpContent content, Action<JObject> processResult, ILogger log)
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// content.Headers.Clear();
content.Headers.Add("Content-Type", "application/json");
try
{
HttpResponseMessage response = await httpClient.PostAsync(url, content);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject(json) as JObject;
processResult(result);
}
else
{
var errorContent = await response.Content.ReadAsStringAsync();
log.LogError(errorContent);
}
}
catch (System.Exception ex)
{
log.LogError(ex, ex.Message);
throw;
}
}
Here is an example of the calling code
public async Task SendInvitation(string token, Invitation invitation, ILogger logger)
{
var stringContent = new StringContent(JsonConvert.SerializeObject(invitation), Encoding.UTF8, "application/json");
await HttpHelpers.PostAsync(
Client,
"https://graph.microsoft.com/v1.0/invitations",
token,
stringContent,
result => logger.LogInformation(DebugHelpers.Print(result)),
logger);
}
Answered (Sort of)
If I change
HttpResponseMessage response = await httpClient.PostAsync(url, content);
to
HttpResponseMessage response = httpClient.PostAsync(url, content).GetAwaiter().GetResult();
It seems to work but it's slow because what I'm doing is using blocking code. I think this is a quirk of core 3 on macOS. I don't like that this is happening.
More Info
I'm doing a lot of looping.
It seems that if I put all the things I'm awaiting in a taskList it behaves properly.
\\ Pseudo Code
var taskList = new List<Task>();
foreach(var thing in things){
taskList.Add(HttpHelpers.PostAsync(...things));
}
await Task.WhenAll(taskList);
Please check whether all calls you make in the code execution path support asynchronousity. For example once I spent quite some time figuring out a nuget package called CommandLineParser did not support async calls. I was using it like so :
public static void Main(string[] args)
{
Parser.Default.ParseArguments<Options>(args)
.WithParsed(async options =>
{ await httphelper.PostAsync(...);
}
}
I fixed the issue by changing it to something like
public static void Main(string[] args)
{
Parser.Default.ParseArguments<Options>(args)
.WithParsed(options =>
{ httphelper.PostAsync(...).Result;
}
}
So please check you are not using some calls that do not support async in the way.
You can wait for HttpResponseMessage certain condition. Inspect HttpResponseMessage in debug, with 2 given steps. Screenshot of debug process: postAsync debug
return HttpResponseMessage from method with postAsync:
private static async Task<HttpResponseMessage> methodWithPostAsync(){
...
response = await client.PostAsync(url, data);
return response
}
call method and wait for response message status:
Task<HttpResponseMessage> content= methodWithPostAsync();
while (!content.IsCompleted)
{
Console.WriteLine("busy");
System.Threading.Thread.Sleep(1000);
}
try ConfigureAwait like this
HttpResponseMessage response = await httpClient.PostAsync(url, content).ConfigureAwait(false);
I have an IO bound method that hangs for a split second while fetching data. I've attempted to convert the method to an async method but am having issues with this.
I've included below the non-async version of the code, and my attempt at making it async.
//non async method
public double GetBaseline()
{
var Client = new RestClient();
IRestResponse response;
Client.BaseUrl = new Uri("https://apiv2.bitcoinaverage.com/indices/global/ticker/short?crypto=BTC&fiat=USD");
CryptoAverage BTCAvg;
var request = new RestRequest();
response = Client.Execute(request);
BTCAvg = JsonConvert.DeserializeObject<CryptoAverage>(response.Content);
return Math.Round(BTCAvg.BTCUSD.Last, 2);
}
//async method
public async double GetBaselineAsync()
{
var Client = new RestClient();
IRestResponse response;
Client.BaseUrl = new Uri("https://apiv2.bitcoinaverage.com/indices/global/ticker/short?crypto=BTC&fiat=USD");
CryptoAverage BTCAvg;
var request = new RestRequest();
response = await Client.ExecuteAsync(request);
BTCAvg = JsonConvert.DeserializeObject<CryptoAverage>(response.Content);
return Math.Round(BTCAvg.BTCUSD.Last, 2);
}
There are two issues that I know of with the above code. The first line requires some some of Task keyword but I'm unsure how to code it. I've tried a number of things here without success.
Secondly, ExecuteAsync takes a 2nd argument but I'm unsure what. I've seen some examples of this, but they seem overly complicated for what I'm trying to do?
Appreciate any help you guys can offer!
If you want to use the Async Await pattern, you need to declare the method appropriately
public async Task<double> GetBaselineAsync()
{
var Client = new RestClient();
IRestResponse response;
Client.BaseUrl = new Uri("https://apiv2.bitcoinaverage.com/indices/global/ticker/short?crypto=BTC&fiat=USD");
CryptoAverage BTCAvg;
var request = new RestRequest();
response = await Client.ExecuteAsync(request);
BTCAvg = JsonConvert.DeserializeObject<CryptoAverage>(response.Content);
return Math.Round(BTCAvg.BTCUSD.Last, 2);
}
Usage
await GetBaselineAsync();
Yes.. You will have to let your async propagate through your code like a virus, or not use it at all.
Secondly, if ExecuteAsync is taking 2 seconds, its not because of .Net, there is not reason why the compiler would make your code pause for 2 seconds because of an await (even if you aren't using it correctly) Its the the call to the internet
If the library you use supports both sync and async methods(last usually with Async suffix) you should always use async ones. Async will create separate threads in your code in order to have better performance. And it will run concurrently when it needs. Async methods should almost in every case return Task or generic Task<>. If you want to call an asynchronous method in the synchronous method (in case of method signature doesn't allow you to use await keyword) you need to use .GetAwaiter().GetResult() upon a method which returns Task(in this case it will block the thread and won't run concurrently). Google around it worse it to spend time on it. Async await pattern was a huge step up of C# as language
EDIT: The solution was to change the declaration to;
public async Task<double> GetBaselineAsync()
And change ExecuteAsync to ExecuteTaskAsync. Full code;
public async Task<double> GetBaselineAsync()
{
var Client = new RestClient();
IRestResponse response;
Client.BaseUrl = new Uri("https://apiv2.bitcoinaverage.com/indices/global/ticker/short?crypto=BTC&fiat=USD");
CryptoAverage BTCAvg;
var request = new RestRequest();
response = await Client.ExecuteTaskAsync(request);
BTCAvg = JsonConvert.DeserializeObject<CryptoAverage>(response.Content);
return Math.Round(BTCAvg.BTCUSD.Last, 2);
}
I'm trying to build a dynamic http client using dynamic proxy to intercept the calls and create an http request with it.
The issue i had was with Async methods:
private Task<object> PostAsync(HttpClient client, string url, HttpRequestParameters parameters, Type returnType)
{
return Task.Run(async () =>
{
var requestContent = new StringContent(Serializer.Serialize(parameters.BodyParameters));
var httpResponse = await client.PostAsync(url, requestContent);
var responseContent = await httpResponse.Content.ReadAsStringAsync();
return Serializer.Deserialize(responseContent, returnType);
});
}
My task returns dynamic/object and not the T of the Interception return type.
I thought that i will be able to use it like so
var task = PostAsync(client, url, parameters, returnType);
invocation.ReturnValue = task;
Since the task that will be returned is the original task and it is still pending i thought it would work but all i'm getting is an exception that Task cant be converted to task of my type (Which is string in that case).
Thanks for the helpers
Edit:
I did see Intercept async method, that's what i tried to do but i was unable to call the Task even using reflection, i still got the same exception.
I solved it eventually with a few modifications:
Creating the interceptor with a base object, i used Moq objects to lazy create them and store them in a ConcurrentDictionary for caching.
var mock = new Mock<T>();
var pg = new ProxyGenerator();
return pg.CreateInterfaceProxyWithTarget<T>(GetTarget(clientType), _gatewayInterceptor);
I passed the invocation's return value (in that case Task of T) to a method and got the T.
I wrapped the http call with a new Task of T, await the http call and
return desirialized T result from the task.
Assign the new Task of T back to the return value.
invocation.ReturnValue = GetAsync((dynamic)invocation.ReturnValue, serializer, headers, req);
internal static Task<T> GetAsync<T>(Task<T> originalTask, ISerializer serializer, Headers headers, InvokeHttpRequest req)
{
return Task.Run(async () =>
{
using (HttpClient client = new HttpClient())
{
var httpResponse = await PerformGetAsync(headers, req, client);
var jsonResponse = await httpResponse.Content.ReadAsStringAsync();
return ProcessResult<T>(serializer, jsonResponse);
}
});
}
I know its not the best way to go but it worked for me.
The solution is here if anyone needs it https://github.com/ErezLevip/SimpleProxyClient
Hello I dont get the difference between the following two asnyc functions, could someone explain it to me? Both of them doesnt return IRestResponse, so I cant access StatusCode field. Do I have to cast here?
public async Task<IRestResponse> Post<T>(string Ressource, T ObjTOPost) where T : new()
{
return await Task.Factory.StartNew(() =>
{
var client = new RestClient("test.com");
var request = new RestRequest(Ressource, Method.POST);
var response = client.Execute(request);
return response;
});
}
And this:
public async Task<IRestResponse> Post<T>(string Ressource, T ObjTOPost) where T : new()
{
var client = new RestClient("test.com");
var request = new RestRequest(Ressource, Method.POST);
var response = await client.ExecuteTaskAsync<T>(request);
return response;
}
Both of them doesnt return IRestResponse, so I cant access StatusCode field.
They return a Task<IRestResponse>. You can get the interface by awaiting the task, e.g.
var task = Post(resource, objectToPost);
IRestResponse response = await task;
Or in one line (more common):
var response = await Post(resource, objectToPost);
Difference between these two async functions
The second example is far more straightforward. The first example spins up an additional task and passes its awaitable back to the caller, whereas the second example awaits the RestClient directly. I see no reason to use the structure in the first example.
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;
}