Unclear about what to pass through a given Action<T,R> - c#

I am attempting to implement the library RestSharp.Core in a .NET Core application; But it has no documentation.
I need to implement this method;
public virtual RestRequestAsyncHandle ExecuteAsyncPost(IRestRequest request, Action<IRestResponse, RestRequestAsyncHandle> callback, string httpMethod)
I cannot figure out what to pass for the second parameter. This is the code I was using in normal RestSharp before I had to start converting it to .NET Core.
var httpResponse = await httpClient.ExecutePostTaskAsync(httpRequest);
var deserializer = new RestSharp.Deserializers.JsonDeserializer();
return deserializer.Deserialize<Dictionary<string, string>>(httpResponse);
Any ideas? I'm really confused.

RestSharp uses a callback-style kind of asynchrony which is very common in other platforms (e.g., Node), but never really caught on in .NET.
In order to implement ExecuteAsyncPost, you will need to take the request and httpMethod parameters to start an asynchronous post and then return a RestRequestAsyncHandle representing that operation.
Then, when the post completes, you should build an IRestResponse instance and pass that along with the RestRequestAsyncHandle to the callback. The RestRequestAsyncHandle you pass to callback should be the same instance as the one you already returned from ExecuteAsyncPost.
Since RestRequestAsyncHandle appears to be built around HttpWebRequest, you should be able to do something like:
public override RestRequestAsyncHandle ExecuteAsyncPost(IRestRequest request, Action<IRestResponse, RestRequestAsyncHandle> callback, string httpMethod)
{
HttpWebRequest webRequest = /* Construct webRequest from request/httpMethod */
var result = new RestRequestAsyncHandle(webRequest);
DoWebRequest(webRequest, result, callback);
}
private async void DoWebRequest(HttpWebRequest webRequest, RestRequestAsyncHandle result, Action<IRestResponse, RestRequestAsyncHandle> callback)
{
IRestResponse response;
try
{
var webResponse = await webRequest.GetResponseAsync().ConfigureAwait(false);
response = /* Construct an IRestResponse using webResponse */
}
catch (Exception ex)
{
response = /* Construct an IRestResponse with error information */
}
callback(response, result);
}
Note that I'm purposely using async void here because we're implementing a callback-based asynchronous system, and I want exceptions from the callback method to be handled at a top level. In the vast majority of normal async/await usage, you should avoid async void.

The second parameter is an Action (method) that takes a RestRequestAsyncHandle and returns a IRestResponse.
In other words, this method is looking for a method to call as a call back when it's done.

Related

Best practice for making a HTTP Post from asp.net Page_Load?

Seems like simple task, but the interwebs are flooded with different ways to approach this, so what is best practice for making a simple HTTP POST from asp.net Page_Load (I'm on .net 4.7) ?
Many resources point to the fact that HttpClient.PostAsync is the most lightweight approach.
However, the example here: https://learn.microsoft.com/en-us/aspnet/web-forms/overview/performance-and-caching/using-asynchronous-methods-in-aspnet-45 - uses WebClient.
Also, both of theses approaches require setting Page Async="true" on the page - I am seeing conflicting information on whether this is actually the right approach.
Context: My page looks like it is spending a lot of time in BLOCKED_TIME during two requests that are made in Page_Load.
I suspect this is due to the way I currently make the POST:
public string makePost(string parametersToPassOn, string command)
{
HttpContent c = new StringContent(parametersToPassOn, Encoding.UTF8, "application/json");
var t = Task.Run(() => fireRESTcall(new Uri(walletProxyURL + command), c));
t.Wait();
return t.Result;
}
static async Task<String> fireRESTcall(Uri u, HttpContent c)
{
var response = string.Empty;
using (var client = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = u,
Content = c
};
HttpResponseMessage result = await client.SendAsync(request);
if (result.IsSuccessStatusCode)
{
response = await result.Content.ReadAsStringAsync();
}
}
return response;
}
Any help/thoughts would be greatly appreciated.
During the time your page makes an outgoing HTTP request, it can't do anything useful other than waiting for the response. See also Understanding BLOCKED_TIME in PerfView
You probably can't do anything about this delay, as you need the external API for some data, and you need to wait for it to complete in order to do something useful with that data.
So it's advisable to use async/await all the way, using RegisterAsyncTask():
void Page_Load()
{
RegisterAsyncTask(new PageAsyncTask(CallApiAsync));
Page.ExecuteRegisteredAsyncTasks();
}
async Task CallApiAsync() // calls
async Task<string> MakePost() // calls
async Task<String> FireRESTcall()
This frees up the thread to handle a new incoming request, until the outgoing request is finished and the incoming request continues on the same or another thread pool thread.
For this to work with WebForms, you do need Async="true" on your page.

How to propagate HTTP responses cleanly to consumers of a typed HTTP client in ASP.NET Core

Is there a good way of propagating an object on a successful response or the status code and response body to consumers of a typed HTTP client in ASP.NET Core?
Given the following API service:
public class TestApiService
{
private readonly HttpClient _httpClient;
public TestApiService(HttpClient httpClient)
{
httpClient.BaseAddress = new Uri("https://localhost:5000");
_httpClient = httpClient;
}
public async Task<string> GetVersion()
{
var response = await _httpClient.GetAsync("/api/v1/version");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
return null;
}
}
which is registered with the DI container via:
services.AddHttpClient<TestApiService>();
I would like to return the string value from the TestApiService.GetVersion() method if the response was successful or if the response was not successful return the status code and the response body.
It doesn't appear to be possible to do something like:
public async Task<string> GetVersion()
{
var response = await _httpClient.GetAsync("/api/v1/version");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
and get the desired outcome because the HttpRequestException thrown from HttpResponseMessage.EnsureSuccessStatusCode() does not include the status code or the response body.
There is an open issue about this on GitHub but I'm not sure if it will get implemented anytime soon or not.
While ActionResult does exist it seems to really be for the controller layer so I'm not sure if using it here is an appropriate use of that class or not or if there is a better way of getting the desired outcome?
It should be possible to create my own exception class and throw that from the service but I would really like to avoid that if there is some built-in mechanism that is usable instead.
Remove response.EnsureSuccessStatusCode() this is basically checking the status and if its not a 200 throwing the exception. Consider using response.IsSuccessStatusCode or check the response status code manually. Either way will prevent the raising of the exception which you don't want.
if (HttpStatusCode.Ok == response.StatusCode)
{
// Read your result
}
else if ( // handle the specific failure case was it a 404 or a 401)
{
string value = await response.Content?.ReadAsStringAsync();
// Read your failed result
return $"{response.StatusCode} {value}".Trim()";
}
The next question is how you handle and communicate failure to the callee's of your service? Do you want your service to be opaque to your client application?
Since your code is only returning a string, have you considered either returning something else such as an encompassing object { Success = true|false, Error = "", ErrorCode = 1234, Data = "value"} or simply throwing an appropriate exception to communicate the nature of the failure. E.g. You might want to throw an appropriate exception, e.g. TestApiException where TestApiException might have the ErrorCode or whatever you need on it.

Best way to get callback value back to the function that initiated async method?

I'm new at using callbacks and async functions, so I'm a bit unsure of the best way to approach this.
I created a function called SendPhoto() that is called by my GUI. The SendPhoto() function is in a separate class from my GUI.
public string SendPhoto(string filename)
{
byte[] response = PostFile(_url, filename);
String responsestring = Encoding.ASCII.GetString(response);
if (responsestring.StartsWith("ERROR:"))
return responsestring;
else if (responsestring.Contains("<valid>1</valid>"))
return "OK";
else
return responsestring;
}
My PostFile() function used to call WebClient.UploadFile(), and the response was returned to SendPhoto(), and it worked great. Then I decided I wanted to send the photo asynchronously, so in my PostFile() function, I changed the call from Uploadfile() to UploadFileAsync().
However, I realized that UploadFileAsync() doesn't return a value, and I have to use a UploadFileCompletedEventHandler to get the response when it's done uploading. So, I wrote a callback function in the same class as SendPhoto() and PostFile() to retrieve the response, instantiated a UploadFileCompletedEventHandler on that function, and passed it into my PostFile() function.
The problem is that I'm not sure how the get the response back to the SendPhoto() function, so that it can interpret the response and send the friendly response back to the GUI. Before, when everything was synchronous, the response was just passed back up the stack, but now, the response is coming back a couple layers removed.
What is the best way to get the response back from the callback function to SendPhoto(), now that PostFile() can no longer return it to me? I thought of moving the event handler callback to the GUI and passing the UploadFileCompletedEventHandler to SendPhoto(), which would in turn send it to PostFile(). But I am trying to keep "business logic" (i.e. interpreting the response) out of the GUI class.
Ok, worked on it some more this morning, and found a very elegant solution by using "await" (thanks, Muctadir Dinar!). I had to change my call to UploadFileTaskAsync(), in order for it to support the "await" keyword, and I had to decorate all of my methods with "async" and make them return task, all the way back to the GUI's button click event handler, but when I was done, it worked great! I believe this will only work in the .NET framework 4.5.
private async void UploadPhotoButton_Click(object sender, EventArgs e)
{
...
string theResult = await MyProvider.SendPhotoAsync(pathToFile, new UploadProgressChangedEventHandler(UploadProgressCallback));
OutputBox.Text = theResult;
}
public async Task<string> SendPhotoAsync(string filename, UploadProgressChangedEventHandler changedHandler)
{
byte[] response = await PostFileAsync(_url, filename, changedHandler);
String responsestring = Encoding.ASCII.GetString(response);
if (responsestring.StartsWith("ERROR:"))
return responsestring;
else if (responsestring.Contains("<valid>1</valid>"))
return "OK";
else
return responsestring;
}
async Task<byte[]> PostFileAsync(string uri, string filename, UploadProgressChangedEventHandler changedHandler)
{
byte[] response = null;
using (WebClient client = new WebClient())
{
client.Headers = GetAuthenticationHeader();
client.UploadProgressChanged += changedHandler;
response = await client.UploadFileTaskAsync(new Uri(uri), filename);
}
return response;
}

C# Obtaining data from WebRequest not returning values

I have the following C# method that accepts a URL as an input and returns the text data that exists at that location:
public string GetWebData(string uri)
{
string response = string.Empty;
try
{
var request = WebRequest.Create(uri);
request.BeginGetResponse(result =>
{
var httpRequest = (HttpWebRequest)result.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(result);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
response = reader.ReadToEnd();
}
}, request);
}
catch (WebException)
{
response = string.Empty;
}
return response;
}
However, the reader.ReadToEnd(); method returns an empty string. I'm not sure if I'm doing anything wrong, since the method seems to be syntactically identical to all the tutorials I've consulted. What am I doing wrong?
You're returning response immediately - even though the callback which assigns a useful value to response will only fire later. Do you understand how BeginGetResponse works? It would be worth studying the documentation and examples carefully.
Why are you using asynchronous methods if you actually want to return the value as soon as the method finishes? If asynchronous methods are all you've got (e.g. you're on Windows Phone) then you should stick with that asynchronous idiom - don't try to make it synchronous.
As an aside, swallowing exceptions like this is a really bad idea too - you should almost never continue as if everything's okay, and even if you do want to ignore the error as far as the user experience is concerned, you should almost certainly be logging the exception.
Additionally, when you fetch the response you should use a using statement as WebResponse implements IDisposable.

Example of RestSharp ASYNC client.ExecuteAsync<T> () works

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;

Categories

Resources