Luke
Luke

Reputation: 21236

Retrieving JSON content by calling a REST API with C# and HttpClient

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

Answers (2)

Rohit Vipin Mathews
Rohit Vipin Mathews

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

Jason
Jason

Reputation: 89082

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

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

Upvotes: 0

Related Questions