Reputation: 21236
I'm having a stab at some mobile development with Xamarin and C#. I'm building a simple login screen for an Android app and I'm using the HttpClient
to make the actual calls but I'm stuck on some of the details to get it to work.
I have set up a simple Client
class that represents my API client:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace Acme.Api
{
public class Session
{
public string Token;
public int Timeout;
}
public class Client
{
public async Task<string> authenticate( string username, string password )
{
using (var client = new HttpClient ())
{
string content = null;
client.BaseAddress = new Uri ("https://example.com/api/");
client.DefaultRequestHeaders.Accept.Clear ();
client.DefaultRequestHeaders.Accept.Add (new MediaTypeWithQualityHeaderValue ("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue ("Basic", string.Format ("{0}:{1}", username, password));
HttpResponseMessage response = await client.PostAsync ("auth", null);
content = await response.Content.ReadAsStringAsync();
return content;
}
}
}
}
As you can see, the authenticate
method uses a POST
to create a new session on the server. The /auth
endpoint returns a JSON blob with a token and a timeout value.
This is how the authenticate
method is called:
Client myClient = new Client();
Task<string> contentTask = myClient.authenticate( username, password );
var content = contentTask.ToString();
Console.Out.WriteLine(content);
My content
never outputs anything. I'm obviously (and without a doubt) doing various things wrong.
How can I have my authenticate
method return the JSON string I expect?
I have been using these sources for inspiration:
http://developer.xamarin.com/guides/cross-platform/advanced/async_support_overview/ http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
Upvotes: 1
Views: 5618
Reputation: 11787
Since you are returning a Task
and not just a string
you need to wait upon the myclient.authenticate
method.
You wouldnt have to wait if you retruned a string
. Which I think is possible in your case, by just changing the return type from Task<string>
to string
.
your Clent authenticate
method :
public class Client
{
public async Task<string> authenticate( string username, string password )
{
using (var client = new HttpClient ())
{
string content = null;
client.BaseAddress = new Uri ("https://example.com/api/");
client.DefaultRequestHeaders.Accept.Clear ();
client.DefaultRequestHeaders.Accept.Add (new MediaTypeWithQualityHeaderValue ("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue ("Basic", string.Format ("{0}:{1}", username, password));
HttpResponseMessage response = await client.PostAsync ("auth", null);
//deserialize json, not sure if you need it as its not an object that you are returning
jsonstring = await response.Content.ReadAsStringAsync();
var content = JsonConvert.DeserializeObject<string>(jsonstring);
return content;
}
}
}
Consume the Client as :
async void btn_click(string username,string password)
{
// This should be an async method as well
Client myClient = new Client();
// added await
string content = await myClient.authenticate(username, password);
Console.Out.WriteLine(content);
}
The sample code is already present in the source of inspiration
GetButton.Click += async (sender, e) => {
Task<int> sizeTask = DownloadHomepage();
ResultTextView.Text = "loading...";
ResultEditText.Text = "loading...\n";
// await! control returns to the caller
// "See await"
var intResult = await sizeTask
// when the Task<int> returns, the value is available and we can display on the UI
ResultTextView.Text = "Length: " + intResult ;
// "returns" void, since it's an event handler
};
public async Task<int> DownloadHomepage()
{
var httpClient = new HttpClient(); // Xamarin supports HttpClient!
Task<string> contentsTask = httpClient.GetStringAsync("http://xamarin.com"); // async method!
// await! control returns to the caller and the task continues to run on another thread
// "See await"
string contents = await contentsTask;
ResultEditText.Text += "DownloadHomepage method continues after async call. . . . .\n";
// After contentTask completes, you can calculate the length of the string.
int exampleInt = contents.Length;
ResultEditText.Text += "Downloaded the html and found out the length.\n\n\n";
ResultEditText.Text += contents; // just dump the entire HTML
return exampleInt; // Task<TResult> returns an object of type TResult, in this case int
}
Upvotes: 4
Reputation: 89082
authenticate() is an async method, so you need to await it
string content = await myClient.authenticate( username, password );
Upvotes: 0