1

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

2 Answers 2

4

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
}
Sign up to request clarification or add additional context in comments.

Comments

0

authenticate() is an async method, so you need to await it

string content = await myClient.authenticate( username, password );

1 Comment

Isn't the await already used "inside" the authenticate method? Also, placing await in front the authenticate method seems to be a syntax error in the Xamarin IDE (the error is in a tooltip which I can't seem to copy).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.