C# Get Request (API) - How to Callback? (Delegate?) - c#

I think for what I want to do, I need some kind of callback/delegate. For now Is spend about 8 hours reading stuff about those callbacks an watched some youtube videos, but still I don't fully understand how they work. (Newer used callbacks in any language till now.)
On button-click I call the function:
private void btnLoad_Click(object sender, EventArgs e)
{
GetRequest("http://192.168.68.127/axis-cgi/param.cgi?action=list&group=MediaClip", "root", "root");
}
Here is the function which is use to get the result of the request:
public static async void GetRequest(string url, string user, string pass)
{
using (HttpClientHandler handler = new HttpClientHandler { Credentials = new System.Net.NetworkCredential(user, pass) })
{
using (HttpClient client = new HttpClient(handler))
{
using (HttpResponseMessage response = await client.GetAsync(url))
{
using (HttpContent content = response.Content)
{
string mycontent = await content.ReadAsStringAsync();
// --- Do different stuff with "mycontent", depending which button was clicked ---
// --- Insert function here ?! ---
}
}
}
}
}
Now my question is, how can I tell "GetRequest(string url, string user, string pass)" to execute a specific function on the place I need it?
I think I would need sth. like:
GetRequest(string url, string user, string pass, function())

With async-await you don't need use a "callbacks"
Change your method to return "awaitable" content.
public static async Task<string> GetRequest(string url,
string user,
string pass)
{
var credentials = new System.Net.NetworkCredential(user, pass);
using (var handler = new HttpClientHandler { Credentials = credentials })
{
using (var client = new HttpClient(handler))
{
using (var response = await client.GetAsync(url))
{
using (HttpContent content = response.Content)
{
return await content.ReadAsStringAsync();
}
}
}
}
}
Notice return type of the method Task<string>
Then use it anywhere you want
string content = await GetRequest("url", "admin", "admin");
// Do staff with content

Define a delegate, which defines the signature of the methods
belong to the delegate. In this case: return value void and no
parameters:
public delegate void YourDelegate();
Define the callback function which should be used for GetRequest:
public void CallbackFunction() {...}
Define a parameter in the GetRequest method to use the callback function:
public void GetRequest(string url, string user, string pass, YourDelegate callback) {...}
Invoke the callback in GetRequest:
callback.Invoke();
Example for executing GetRequest:
GetRequest("http://192.168.68.127/axis-cgi/param.cgi?action=list&group=MediaClip", "root", "root", CallbackFunction);

Related

C# stops working when performing async post request

I am working on a mobile app, and the problem is that when I perform a async request ( PostAsync ) using Net.Http my program stops running.
Here is my request class, where I perform the requests using Net.Http.
...
namespace BSoft.Requests
{
public class Requests
{
public Requests(){}
public static string HostName = "https://dev5.360businesssoft.com/";
private static readonly HttpClient httpClient = new HttpClient();
public static async Task<string> PerformPostRequest(Dictionary<string, string> values, string path)
{
string url = HostName + path;
FormUrlEncodedContent content = new FormUrlEncodedContent(values);
HttpResponseMessage response = await httpClient.PostAsync(url, content);
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
}
}
and here is my login class, where I call call the request and display the result as string.
...
namespace BSoft.Login
{
public class Login
{
public Login()
{
}
public static void PerformLogin(string username, string password, bool remember)
{
var values = new Dictionary<string, string>();
values.Add("User", username);
values.Add("Password", password);
var ReturnedObj = Requests.Requests.PerformPostRequest(values, "test.php").Result;
System.Diagnostics.Debug.WriteLine(ReturnedObj);
}
}
}
This is a screenshot of the app, you can notice that the button is freezed
The call to Result is blocking the gui thread. Instead, await the result:
var ReturnedObj = await Requests.Requests.PerformPostRequest(values, "test.php");
System.Diagnostics.Debug.WriteLine(ReturnedObj);
Your call to Result will block the gui thread until PerformPostRequest completes, so there's not really a lot of point using the async features here. If you really don't want the code to execute asynchronously then you might as well remove the calls to the async methods and make the calls synchronous.
Try
string returnedString = await Requests.Requests.PerformPostRequest(values, "test.php");

HttpClient Async Method return value

I studied over the Internet regarding Task Async method but cannot seem to find an approach to assign my return value in Task Async to another object. The first method is to prepare HTTP Request header and Uri.
public static async Task MainAsync()
{
string token = await AuthHelper.AcquireToken(tenantId, clientId, clientSecret);
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
client.BaseAddress = new Uri("https://foo.net");
await GetValue(client);
}
}
The second method is to use GetAsync to call to an API to get the JSON and the two last lines I extract only value from the "Value" field in the JSON body.
public static async Task<String> GetValue(HttpClient client)
{
string url = $"/mykey/key01";
using (var httpResponse = await client.GetAsync(url))
{
httpResponse.EnsureSuccessStatusCode();
string responsContent = await httpResponse.Content.ReadAsStringAsync();
JObject json = JObject.Parse(responsContent);
string value = json["value"].ToString();
return value;
}
}
Now I would like to use this value to assign to another object, but not sure how to do so. I managed to return the valid value. Is it possible to retrieve the value from another method or even different class?
[Updated] The main function is:
static void Main(string[] args)
{
try
{
MainAsync().Wait();
}
catch (Exception e)
{
Console.WriteLine(e.GetBaseException().Message);
}
}
Update
To be more clear. The HTTP response message is a JSON format and I can return the value from Value property in this JSON. Now how I can to reuse the value from an external method or class
I'm not sure exactly what you are trying to achieve. And there would be thorough debates about your architecture, you can do something like this..
Update
Because your MainAsync is static it can be called form anywhere.
You just need to modify it a bit to return your result as follows :
public static async Task<string> MainAsync()
{
...
return await GetValue(client);
...
And somewhere else
public class MyAwesomeClass
{
public async Task DoMagic()
{
var newValueOfSomething = await MainAsync();
// hilarity ensues
}
}
You can Make it more generic and useful, something like below :
Your initial method can be changes to :
public async Task<T> GetContentAsync<T>(HttpClient client)
{
string url = $"/mykey/key01";
using (var httpResponse = await client.GetAsync(url))
{
httpResponse.EnsureSuccessStatusCode();
string responsContent = await httpResponse.Content.ReadAsStringAsync();
return Deserialize<T>(json);
}
}
private T Deserialize<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json, SerializationSettings);
}
You can now call method like :
var person = await GetContentAsync<Person>(/*http client*/)

Converting the content of HttpResponseMessage to object

My Question: How do I do this?
So, I hadn't touched anything .Net in about 6 years until this week. There's a lot that I've forgotten and even more that I never knew and while I love the idea of the async/await keywords, I'm having a slight problem implementing the following requirements for a client's API implementation:
The ServerAPI class has a method for each of the API methods, taking appropriate input parameters (e.g. the method Login takes in an id and a password, makes the API call and returns the result to the caller).
I want to abstract away the JSON so that my API methods return the actual object you're fetching (e.g. the Login method above returns a User object with your auth token, uid, etc.)
Some API methods return a 204 on success or no meaningful content (not meaningful in my usecase maybe I only care about success/failure), for these I'd like to return either a bool (true = success) or the status code.
I'd like to keep the async/await (or equivalent) design, because it seems to really work well so far.
For some methods, I might need to just return the HttpResponseMessage object and let the caller deal with it.
This is roughly what I have so far and I'm not sure how to make it compliant with the above OR whether I'm even doing this right. Any guidance is appreciated (flaming, however, is not).
// 200 (+User JSON) = success, otherwise APIError JSON
internal async Task<User> Login (string id, string password)
{
LoginPayload payload = new LoginPayload() { LoginId = id, Password = password};
var request = NewRequest(HttpMethod.Post, "login");
JsonPayload<LoginPayload>(payload, ref request);
return await Execute<Account>(request, false);
}
// 204: success, anything else failure
internal async Task<Boolean> LogOut ()
{
return await Execute<Boolean>(NewRequest(HttpMethod.Delete, "login"), true);
}
internal async Task<HttpResponseMessage> GetRawResponse ()
{
return await Execute<HttpResponseMessage>(NewRequest(HttpMethod.Get, "raw/something"), true);
}
internal async Task<Int32> GetMeStatusCode ()
{
return await Execute<Int32>(NewRequest(HttpMethod.Get, "some/intstatus"), true);
}
private async Task<RESULT> Execute<RESULT>(HttpRequestMessage request, bool authenticate)
{
if (authenticate)
AuthenticateRequest(ref request); // add auth token to request
var tcs = new TaskCompletionSource<RESULT>();
var response = await client.SendAsync(request);
// TODO: If the RESULT is just HTTPResponseMessage, the rest is unnecessary
if (response.IsSuccessStatusCode)
{
try
{
// TryParse needs to handle Boolean differently than other types
RESULT result = await TryParse<RESULT>(response);
tcs.SetResult(result);
}
catch (Exception e)
{
tcs.SetException(e);
}
}
else
{
try
{
APIError error = await TryParse<APIError>(response);
tcs.SetException(new APIException(error));
}
catch (Exception e)
{
tcs.SetException(new APIException("Unknown error"));
}
}
return tcs.Task.Result;
}
This is the APIError JSON structure (it's the status code + a custom error code).
{
"status": 404,
"code":216,
"msg":"User not found"
}
I would prefer to stay with System.Net, but that's mostly because I don't want to switch all my code over. If what I want is easier done in other ways then it's obviously worth the extra work.
Thanks.
Here is an example of how I've done it using MVC API 2 as backend. My backend returns a json result if the credentials are correct. UserCredentials class is the exact same model as the json result. You will have to use System.Net.Http.Formatting which can be found in the Microsoft.AspNet.WebApi.Client NugetPackage
public static async Task<UserCredentials> Login(string username, string password)
{
string baseAddress = "127.0.0.1/";
HttpClient client = new HttpClient();
var authorizationHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes("xyz:secretKey"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationHeader);
var form = new Dictionary<string, string>
{
{ "grant_type", "password" },
{ "username", username },
{ "password", password },
};
var Response = await client.PostAsync(baseAddress + "oauth/token", new FormUrlEncodedContent(form));
if (Response.StatusCode == HttpStatusCode.OK)
{
return await Response.Content.ReadAsAsync<UserCredentials>(new[] { new JsonMediaTypeFormatter() });
}
else
{
return null;
}
}
and you also need Newtonsoft.Json package.
public class UserCredentials
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
//more properties...
}
i would use a Deserializer.
HttpResponseMessage response = await client.GetAsync("your http here");
var responseString = await response.Content.ReadAsStringAsync();
[Your Class] object= JsonConvert.DeserializeObject<[Your Class]>(responseString.Body.ToString());
So, first to address the you need Newtonsoft.Json comments, I really haven't felt the need yet. I've found the built in support to work well so far (using the APIError Json in my original question:
[DataContract]
internal class APIError
{
[DataMember (Name = "status")]
public int StatusCode { get; set; }
[DataMember (Name = "code")]
public int ErrorCode { get; set; }
}
I have also defined a JsonHelper class to (de)serialize:
public class JsonHelper
{
public static T fromJson<T> (string json)
{
var bytes = Encoding.Unicode.GetBytes (json);
using (MemoryStream mst = new MemoryStream(bytes))
{
var serializer = new DataContractJsonSerializer (typeof (T));
return (T)serializer.ReadObject (mst);
}
}
public static string toJson (object instance)
{
using (MemoryStream mst = new MemoryStream())
{
var serializer = new DataContractJsonSerializer (instance.GetType());
serializer.WriteObject (mst, instance);
mst.Position = 0;
using (StreamReader r = new StreamReader(mst))
{
return r.ReadToEnd();
}
}
}
}
The above bits I already had working. As for a single method that would handle each request execution based on the type of result expected while it makes it easier to change how I handle things (like errors, etc), it also adds to the complexity and thus readability of my code. I ended up creating separate methods (all variants of the Execute method in the original question:
// execute and return response.StatusCode
private static async Task<HttpStatusCode> ExecuteForStatusCode (HttpRequestMessage request, bool authenticate = true)
// execute and return response without processing
private static async Task<HttpResponseMessage> ExecuteForRawResponse(HttpRequestMessage request, bool authenticate = true)
// execute and return response.IsSuccessStatusCode
private static async Task<Boolean> ExecuteForBoolean (HttpRequestMessage request, bool authenticate = true)
// execute and extract JSON payload from response content and convert to RESULT
private static async Task<RESULT> Execute<RESULT>(HttpRequestMessage request, bool authenticate = true)
I can move the unauthorized responses (which my current code isn't handling right now anyway) into a new method CheckResponse that will (for example) log the user out if a 401 is received.

Why does the UI become non-responsive even though I'm using an async method?

I'm currently developing an Android app using the Xamarin framework, so I'm writing it in c#. In this app I'm fetching data from a REST-api, like this:
//Method implemented from interface IOnScrollListener
public async void OnScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
//lots of other stuff that's not important for this example
List<Message> messageList = await PopulateMessagebox(arg1, arg2));
}
The method is being called when you reach the bottom of a list (hence the IOnScrollListener implementation). However; the screen freezes when I reach the bottom, and the method is being invoked. And I'm curious as to how this can be, considering I'm using an ansync method call? Shouldn't it run on another thread, allowing the main thread to be uninterrupted/not having to wait?
How would I go about fixing this, so that the app would be responsive while it, in the background, fetches additional info from the API?
Edit: Here's the populateMessageBox-method
public async Task<List<Message>> PopulateMessagebox(Constants.MMBType type, int skipSize)
{
string messagesJson = await _dal.GetMessageBox(type, skipSize, url);
var mess = new List<Message>();
try
{
if (!string.IsNullOrEmpty(messagesJson))
{
mess = Serializer.DeserializeMessagebox(messagesJson);
}
}
catch (Exception e)
{
Logger.Logg("Failed to de-serialize the messagebox:" + e);
throw new AppException(ErrorMessages.SerializationError);
}
return mess;
}
public async Task<string> GetMessageBox(Constants.MMBType type, int skipSize, string url)
{
return await GetMessageBoxJSON(type, skipSize, url);
}
--
private async Task<string> GetMessageBoxJSON(Constants.MMBType type, int skipSize, string url)
{
string res = null;
if (type == Constants.MMBType.Messagebox)
{
string param = string.Format("?$filter=Status ne '{0}' and Status ne '{1}'&$skip={2}",
Constants.NO.Nob.Status.Sent, Constants.NO.Nob.Status.SentCorres, skipSize);
string request = url + param;
res = await DownloadAndHandle(request);
}
And a couple more calls like this, ends us up here:
protected virtual async Task<string> SetupAndDownloadString(string uri)
{
string responseText;
if (Session.CookieJar.Size() > 0)
{
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("ApiKey", AppContext.ApiKey);
request.Headers.Add("Accept", Constants.ApiFormat);
request.Headers.Add("Cookie", Session.CookieJar.ToString());
HttpResponseMessage response = await client.SendAsync(request);
responseText = response.Content.ReadAsStringAsync().Result;
}
else
{
throw new AppException(ErrorMessages.InsufficientAuthorization);
}
return responseText;
}

Async is becoming a pain in the behind because I'm trying to make a re-usable library that doesn't suck

Because Post requests to APIs need to run asynchronously on windows phone, I am struggling to create a lean easy to use library to interact with an API.
The issue is that people using the library will always need to supply a callback function.
Let's take a look at some pseudo code:
PostRequest Class to help me with POST requests:
class PostRequest
{
private Action<MemoryStream> Callback;
public PostRequest(string urlPath, string data, Action<MemoryStream> callback)
{
Callback = callback;
// Form the URI
UriBuilder fullUri = new UriBuilder(urlPath);
if (!string.IsNullOrEmpty(data))
fullUri.Query = data;
// Initialize a new WebRequest
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(fullUri.Uri);
request.Method = "POST";
// Set up the state object for the async request
DataUpdateState dataState = new DataUpdateState();
dataState.AsyncRequest = request;
// Start the asynchronous request
request.BeginGetResponse(new AsyncCallback(HandleResponse),
dataState);
}
private void HandleResponse(IAsyncResult asyncResult)
{
// Get the state information
DataUpdateState dataState = (DataUpdateState)asyncResult.AsyncState;
HttpWebRequest dataRequest = (HttpWebRequest)dataState.AsyncRequest;
// End the async request
dataState.AsyncResponse = (HttpWebResponse)dataRequest.EndGetResponse(asyncResult);
if (dataState.AsyncResponse.StatusCode.ToString() == "OK")
{
// Create a stream from the response
Stream response = dataState.AsyncResponse.GetResponseStream();
TextReader textReader = new StreamReader(response, true);
string jsonString = textReader.ReadToEnd();
MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
// Send the stream through to the callback function
Callback(stream);
}
}
}
public class DataUpdateState
{
public HttpWebRequest AsyncRequest { get; set; }
public HttpWebResponse AsyncResponse { get; set; }
}
The API Access Object classes:
class APIAuthenticationCredentials
{
public String Username { get; set; }
public String Password { get; set; }
}
class APIAO
{
private String AuthUrl = "http://api.example.com/";
public static Auth Auth = new Auth();
//...
public static void Authenticate( String data, APIAuthenticationCredentials credentials, Action<MemoryStream> callback )
{
PostRequest request = new PostRequest(AuthURL, data, callback);
}
//...
}
You will notice I have to pass a callback function all the way through this so that once the data is returned by the HandleResponse method in my PostRequest class, the data is forwarded onto some controller that makes the screen do something with the data. At the moment, it's not ultra horrid to use:
private void DisplayData(MemoryStream stream)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Auth));
APIAO.Auth = (Auth)serializer.ReadObject(stream);
}
//...
APIAuthenticationCredentials credentials = new APIAuthenticationCredentials {
Username = "whatever",
Password = "whatever"
}
APIAO.Authenticate( credentials, DisplayData );
//...
The problem is I want to create some kind of repository style pattern... Let's say the API returned different json models, one call returned an array of products... the problem is that I want to create one lovely repository call eg:
IProductRepository productRepository = new ProductRepository();
productRepository.GetAll();
But I've gotta put some GOSH DARN callback function in it too and that means every repository method of any object type returned by the API is going to have this MemoryStream callback... and if I ever want to change that functionality, I've gotta update that stuff everywhere yo. :(
Has anyone seen a better way of doing this crap.
This is starting to become far too complex
--crying
A simpler answer using newer language constructs would be:
public static Task<string> GetData(string url, string data)
{
UriBuilder fullUri = new UriBuilder(url);
if (!string.IsNullOrEmpty(data))
fullUri.Query = data;
WebClient client = new WebClient();
client.Credentials = CredentialCache.DefaultCredentials;//TODO update as needed
return client.DownloadStringTaskAsync(fullUri.Uri);
}
In a 4.0 project you can use a TaskCompletionSource to translate a non-Task asynchronous model into a Task:
public static Task<string> GetData2(string url, string data)
{
UriBuilder fullUri = new UriBuilder(url);
if (!string.IsNullOrEmpty(data))
fullUri.Query = data;
WebClient client = new WebClient();
client.Credentials = CredentialCache.DefaultCredentials;//TODO update as needed
var tcs = new TaskCompletionSource<string>();
client.DownloadStringCompleted += (s, args) =>
{
if (args.Error != null)
tcs.TrySetException(args.Error);
else if (args.Cancelled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(args.Result);
};
client.DownloadStringAsync(fullUri.Uri);
return tcs.Task;
}
The caller now has a Task<string> that represents the results of this asynchronous operation. They can wait on it synchronously and get the result using the Result property, they can add a callback that will execute when the operation finishes using ContinueWith, or they can await the task in an async method which, under the hood, will wire up the remainder of that method as a continuation of that task, but without creating a new method or even a new scope, i.e.
public static async Task Foo()
{
string result = await GetData("http://google.com", "");
Console.WriteLine(result);
}
This will start the asynchronous task, add a callback (or continuation) to that task so that when it runs it will continue executing code where it left off, at which point it will then write the results to the console and mark the Task that this method returns as completed, so that any continuations to this method will then execute (allowing for composition of async methods).

Categories

Resources