Beray Bentesen in Cross-Platform

Consuming a RESTful Web Service - Xamarin

This article will be covering :

  • Making HTTP Request
    • Basic Authentication
    • Token Based Authentication
    • Get - Post - Put methods
  • Working with Json formatted data
    • Creating Json classes
    • Creating data model
    • Deserializing data
  • Integrating with the platform
    • Setting up iOS project
    • Setting up Android project
    • Customizing HttpClient per platform
    • iOS native settings
    • Android native settings
  • Performance optimization
    • Instantiating IDisposable object in a using statement
    • ConfigureAwait - Async/Await

Prerequisites

  1. Installing Microsoft HttpClient libraries to PCL
  2. Installing Json.NET libraries to PCL
  3. Adding System.Net.Http to references for both iOS and Android projects.

Basic Authentication

Basic authentication is the most widely used HTTP authentication mechanism to control access to pages and other resources.

  • Create a new empty class within PCL (e.g., HttpTestClass, HttpMethods).
  • Create an async Task to be able to use await keyword. You can copy and paste following code block.
public static async Task basicAuthTask()  
{
  using (var httpClient = new HttpClient())
   {

   }
}  
  • Basic authentication requires AuthenticationHeaderValue which takes base64String as a value. Also you may change url inside Activity / ViewController so let's add 3 strings as a parameter for username, password and url. Username is representative keyword, it might be a phone number for example.
public static async Task basicAuthTask(string userName, string userPassword, string apiUrl)  
{
   using (var httpClient = new HttpClient())
    {

    }
}
  • Setting AuthenticationHeaderValue is easy process but requires byte array as a parameter that must be a combination of user name and password.
        public static async Task basicAuthTask(string userName, string userPassword, string apiUrl)
        {
            using (var httpClient = new HttpClient())
            {
                httpClient.DefaultRequestHeaders.Authorization = 
                new AuthenticationHeaderValue("Basic", Convert.ToBase64String());
            }
        }
  • Copy and paste following method into your class. This method will generate correct byte array and you will be able to use anywhere you need.
public static byte[] convertStringtoByteArray(string userName, string userPassword)  
        {
            var byteArray = Encoding.UTF8.GetBytes(userName + ":" + userPassword);
            return byteArray;
        }
  • Add convertStringtoByteArray method as a parameter to solve error or just replace your code with the following code block.
httpClient.DefaultRequestHeaders.Authorization =  
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(convertStringtoByteArray(userName, userPassword)));  
  • Next step is creating Http request HttpRequestMessage that takes two parameters, HttpMethod and api url. You must set correct HttpMethod in order to prevent response error. Following code uses GET method.
  • You can use POST or PUT method if you are posting / putting any data with required headers.
  • Continue with adding following code block to your code.
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl))  
{

}
  • Most likely you will have to add header to HttpRequestMessage which contains api key and value to pass security.
  • Add following line to HttpRequestMessage and don't forget to change values.
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl))  
{        
     httpRequestMessage.Headers.Add("ApiKey", "KeyValue");
}
  • The last step is sending request message and reading http response.
  • Continue with adding following code block to your code.
using (var httpResponse = await httpClient.SendAsync(httpRequestMessage))  
{
     string readHttpResponse = await httpResponse.Content.ReadAsStringAsync();
}
  • Final and complete code will looks like following code block :
public static async Task basicAuthTask(string userName, string userPassword, string apiUrl)  
{
    using (var httpClient = new HttpClient())
     {
        httpClient.DefaultRequestHeaders.Authorization = 
        new AuthenticationHeaderValue("Basic", Convert.ToBase64String(convertStringtoByteArray(userName, userPassword)));

        using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl))
         {
            httpRequestMessage.Headers.Add("ApiKey", "KeyValue");
           using (var httpResponse = await httpClient.SendAsync(httpRequestMessage))
            {
                string readHttpResponse = await httpResponse.Content.ReadAsStringAsync();
            }
         }
     }
}
  • None of the async methods above don't need the original context.
  • Update your code with ConfigureAwait which allows the continuation to run on a background thread still.
  • ConfigureAwait is more of an optimization hint, has a true as a default value so change it to false like following code block :
public static async Task basicAuthTask(string userName, string userPassword, string apiUrl)  
{
        using (var httpClient = new HttpClient())
        {
            httpClient.DefaultRequestHeaders.Authorization = 
            new AuthenticationHeaderValue("Basic", Convert.ToBase64String(convertStringtoByteArray(userName, userPassword)));

            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl))
             {
                httpRequestMessage.Headers.Add("ApiKey", "KeyValue");
                using (var httpResponse = await httpClient.SendAsync(httpRequestMessage).ConfigureAwait(false))
                 {
                    string readHttpResponse = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
                 }
            }
        }
}
  • Create a simple async method in Activity / ViewController to call basicAuthTask
async void createBasicAuth()  
        {
            string Result = await HttpTestClass.basicAuthTask("userName", "password", "url");
            Console.WriteLine(Result);
        }
  • And call this method with button click :
    public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            testButton.TouchUpInside += (sender, e) => createBasicAuth() ;
        }

Test your code

Token Based Authentication

Token based authentication is prominent everywhere on the web nowadays. With most every web company using an API, tokens are the best way to handle authentication for multiple users.

  • Create a new async Task for no authentication test.
  • This Task requires only one string parameter, api token. Token must be saved after basic authentication.
  • You don't need to set Authorization but you must add api token as an additional header.
  • Following code block represents a simple GET method. Setting ConfigureAwait as a false strongly recommended.
public static async Task NoAuthTask(string apiToken)  
    {
        using (var httpClient = new HttpClient())
        {
            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl))
             {              
                httpRequestMessage.Headers.Add("ApiKey", "KeyValue");
                httpRequestMessage.Headers.Add("ApiToken", apiToken);

                using (var httpResponse = await httpClient.SendAsync(httpRequestMessage))
                 {
                    string readHttpResponse = await httpResponse.Content.ReadAsStringAsync();
                 }
            }
        }
    }
  • If you want to use POST or PUT method, you must create a new FormUrlEncodedContent that takes an array as a parameter.
  • You can add new KeyValuePair as much as you need as shown in following code block :
public static async Task NoAuthTask(string apiToken)  
    {
        using (var httpClient = new HttpClient())
        {
            using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, apiUrl))
             {              
                httpRequestMessage.Headers.Add("ApiKey", "KeyValue");
                httpRequestMessage.Headers.Add("ApiToken", apiToken);

                var content = new FormUrlEncodedContent(new[]
                    {
                        new KeyValuePair<string, string>("key1", "value1"),
                        new KeyValuePair<string, string>("key2", "value2")
                    });

                httpRequestMessage.Content = content;

                using (var httpResponse = await httpClient.SendAsync(httpRequestMessage))
                 {
                    string readHttpResponse = await httpResponse.Content.ReadAsStringAsync();
                 }
            }
        }
    }

Working with Json formatted data

  • Copy your response and visit jsonutils.com
  • Paste your data and click Submit button. You might change Class name optionally. Web site will generate everything you need automatically.
  • Copy result and create a new empty class in PCL. (e.g., TestJsonClass, UserResponseJsonClass).
  • Paste data as shown in following code block.
public class TestJsonClass  
    {
        public class User
        {
            public int id { get; set; }
            public string first_name { get; set; }
            public string last_name { get; set; }
            public long phone { get; set; }
            public int role_id { get; set; }
            public string created { get; set; }
            public string api_token { get; set; }
            public string api_token_expiration { get; set; }
        }

        public class Return
        {
            public string Status { get; set; }
            public string Message { get; set; }
        }

        public class SessionResponse
        {
            public IList<User> User { get; set; }
            public Return Return { get; set; }
        }

        public class Example
        {
            public SessionResponse SessionResponse { get; set; }
        }
    }
  • You need to create a data model to represent the data structures for each of the data feeds.
  • Create a new empty class and define necessary variables.
    public class TestModel
    {
        public string userName { get; set; }
        public string userLastName { get; set; }
        public string httpStatus { get; set; }
        public string httpMessage { get; set; }
    }
  • While working with collections (e.g., RecyclerView, UITableView) they need a List as a source type.
  • Update your Task method and return a List that contains your TestModel.
public static async Task<List<TestModel>> basicAuthTask(string userName, string userPassword, string apiUrl)  
{
    var testModelList = new List<TestModel>();
  • Firstly, parse your result into Json object.
  • Continue with deserializing that json object into your Json Class.
  • Create a TestModel object (e.g., var user).
  • Add this object to List and set correct values.
  • Return List.
using (var httpResponse = await httpClient.SendAsync(httpRequestMessage).ConfigureAwait(false))  
 {
    string returnValue = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);

    var jObject = JObject.Parse(returnValue);

    var jsonObject = JsonConvert.DeserializeObject<TestJsonClass.Example>(jObject.ToString());

    var user = jsonObject.SessionResponse.User[0];

    testModelList.Add(new TestModel
    {
        userName = user.first_name,
        userLastName = user.last_name,
        httpStatus = jsonObject.SessionResponse.Return.Status,
        httpMessage = jsonObject.SessionResponse.Return.Message
    });
 }
  • Final and complete code will looks like following code block :
public static async Task<List<TestModel>> basicAuthTask(string userName, string userPassword, string apiUrl)  
{
    var testModelList = new List<TestModel>();

    using (var httpClient = new HttpClient())
    {
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(convertStringtoByteArray(userName, userPassword)));

        using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, apiUrl))
         {
            httpRequestMessage.Headers.Add("apiKey", "ApiValue");

            using (var httpResponse = await httpClient.SendAsync(httpRequestMessage).ConfigureAwait(false))
             {
                string returnValue = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);

                var jObject = JObject.Parse(returnValue);

                var jsonObject = JsonConvert.DeserializeObject<TestJsonClass.Example>(jObject.ToString());

                var user = jsonObject.SessionResponse.User[0];

                testModelList.Add(new TestModel
                {
                    userName = user.first_name,
                    userLastName = user.last_name,
                    httpStatus = jsonObject.SessionResponse.Return.Status,
                    httpMessage = jsonObject.SessionResponse.Return.Message
                });
            }
        }
    }
    return testModelList;
}
  • If you are not sure how many data will you be getting, you can simply use foreach loop to fill List automatically.
foreach (var user in jsonObject.SessionResponse.User)  
{
    testModelList.Add(new TestModel
    {
        userName = user.first_name,
        userLastName = user.last_name,
        httpStatus = jsonObject.SessionResponse.Return.Status,
        httpMessage = jsonObject.SessionResponse.Return.Message
    });
}
  • Your response would contains many item (e.g., 10+, 100+ ) so your application must take care of it in order to prevent freezing, blocking UI thread etc.
  • Converting JsonConvert.DeserializeObject method to Task and setting ConfigureAwait false is great hack in this situation.
var jsonObject = await Task.Run(() => JsonConvert.DeserializeObject<TestJsonClass.Example>(jObject.ToString())).ConfigureAwait(false);  
  • Create a new List in your Activity / ViewController.
  • Now, you can use your List as you wish.
async void createBasicAuth()  
{
    var testModelList = new List<TestModel>();

    testModelList = await HttpTestClass.basicAuthTask("userName", "password", "url");

    nameLabel.Text = testModelList[0].userName;

    lastNameLabel.Text = testModelList[0].userLastName;

    httpStatusLabel.Text = testModelList[0].httpStatus;

    httpMessageLabel.Text = testModelList[0].httpMessage;
}

Test your code


Customizing HttpClient per platform

Android and iOS both have native networking stacks which are more efficient.

If you want to share your code with PCL but also want use native clients per platform, here is a quick hack.

Before applying / trying following codes you have to check your project settings.

  • Click right to your iOS project > select iOS Build > select HttpClient implementation as NSUrlSession(iOS 7+)
  • Click right to your Android project > select Android Build > select HttpClient implementation as AndroidClientHandler
  • Pass HttpClient as a parameter to your Task as shown in following code block :
public static async Task<List<TestModel>> basicAuthTask(HttpClient httpClient, string userName, string userPassword, string apiUrl)  
{
    var testModelList = new List<TestModel>();

    using (httpClient)
    {
         ... 
  • Create a new HttpClient with new NSUrlSessionHandler() or new AndroidClientHandler() as shown in following code.
async void createBasicAuth()  
{
    var nativeHttpClient  =  new HttpClient(new NSUrlSessionHandler());

    var testModelList = new List<TestModel>();

    testModelList = await HttpTestClass.basicAuthTask(nativeHttpClient,"userName", "password", "url");
    ...
}
  • For Android :
     var nativeHttpClient = new HttpClient(new AndroidClientHandler());

If you have any question or suggestion don't hesitate to comment.