Click here to Skip to main content
Click here to Skip to main content
Go to top

RESTful Made Simple - A Generic REST Service Client Library

, 20 Feb 2012
Rate this:
Please Sign up or sign in to vote.
This article presents a generic REST client library and an example on how to use it.

Download LightCaseClient.zip - 7.97 KB
Download LightCaseClientExample.zip - 29.65 KB

Introduction

This article presents a generic REST service client library and an example on how to use it.

Background

When making a call to a REST service from the web browsers, we have the nicely designed jQuery to make our work simple. But if we call the REST service from a non-browser based client, the work can get a little more complicated. For example, if we want to make a POST call to a REST method which involves both sending and receiving data, and if we want to use the HttpWebRequest object, we need to go through the following steps to complete the call:

  • Create and configure an HttpWebRequest object;
  • Serialize the data to the format that is recognized by the service;
  • Get the Request Stream from the HttpWebRequest object and send the serialized data to the service;
  • Get the HttpWebResponse object from the HttpWebRequest object and further get the Response Stream.
  • Read the data from the response stream and deserialize it to an object of the desired data type.

If you take a look at one of my earlier examples, you should find that calling a REST method is pretty messy. But if you take some closer look into each REST service call, you may find that all the REST calls are very similar. The only difference is that the data type of the data to be sent to the service and the data type of the data received from the service may not be the same in different calls. This makes it ideal to use "Generics" to create a REST client library to simplify the REST calls. This article is to present this generic REST service client library, which I will call it "LightCase" client. Before I wrote this client library, I took a look at the "Web API" and I hope to find something similar, but my brief search did not give me that answer that I was looking for. The "LightCase" client presented here has the following features:

  • The calls to the REST methods can be easily configured by a configuration object;
  • This library gives the applications the option to choose the desired serializers for sending the data to and receiving the data from the service.
  • This library gives the applications the option to send cookies to the REST service to use the Http session state or to go through some security checks at the service;
  • This library supports both synchronous and asynchronous calls;
  • This library is "virtually" thread safe. It is not completely thread safe because I feel that implementing complete thread safety may use more run-time resources. As long as you do not make changes to the configuration object when a REST call is in process, you will not encounter thread safety issues.
I will first show you the "LightCase" client library and then show you an example on how to use it. This article assumes that the readers to have some basic knowledge on REST services, generics, and delegates. If you are new to these topics and if you are interested, you may follow the links in this article to get you started.

The LightCase Client Library

LightCaseSolution.jpg

The client library is implemented as a class library in the above Visual Studio 2010 solution. The "SupportClasses.cs" file implements some supporting classes and interfaces. The generic REST client is implemented in the rest of the files. Let us first take a look at the supporting classes.

The Supporting Classes

The "SupportClasses.cs" file is implemented as the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Web.Script.Serialization;
    
namespace LightCaseClient.Support
{
    public class CookiedHttpWebRequestFactory
    {
        private static Object synchLock = new Object();
        // This dictionary keeps the cookie container for each domain.
        private static Dictionary<string, CookieContainer> containers
            = new Dictionary<string, CookieContainer>();
    
        public static HttpWebRequest Create(string url)
        {
            // Create a HttpWebRequest object
            var request = (HttpWebRequest)WebRequest.Create(url);
    
            // this get the dmain part of from the url
            string domain = (new Uri(url)).GetLeftPart(UriPartial.Authority);
    
            // try to get a container from the dictionary, if it is in the
            // dictionary, use it. Otherwise, create a new one and put it
            // into the dictionary and use it.
            CookieContainer container;
            lock (synchLock)
            {
                if (!containers.TryGetValue(domain, out container))
                {
                    container = new CookieContainer();
                    containers[domain] = container;
                }
            }
    
            // Assign the cookie container to the HttpWebRequest object
            request.CookieContainer = container;
    
            return request;
        }
    }
    
    // Defines an adapter interface for the serializers
    public interface ISerializerAdapter
    {
        string Serialize(object obj);
        T Deserialize<T>(string input);
    }
    
    // An implementation of ISerializerAdapter based on the JavaScriptSerializer
    public class JavaScriptSerializerAdapter : ISerializerAdapter
    {
        private JavaScriptSerializer serializer;
        public JavaScriptSerializerAdapter()
        {
            serializer = new JavaScriptSerializer();
        }
    
        public string Serialize(object obj)
        {
            return serializer.Serialize(obj);
        }
    
        public T Deserialize<T>(string input)
        {
            return serializer.Deserialize<T>(input);
        }
    }
    
    // The configuration class defines how the rest call is made.
    public class ClientConfiguration
    {
        public string ContentType { get; set; }
        public string Accept { get; set; }
        public bool RequrieSession { get; set; }
        public ISerializerAdapter OutBoundSerializerAdapter { get; set; }
        public ISerializerAdapter InBoundSerializerAdapter { get; set; }
    }
    
    // The delegates use for asychronous calls
    public delegate void RestCallBack<T>(Exception ex, T result);
    public delegate void RestCallBackNonQuery(Exception ex);
}  
  • The "CookiedHttpWebRequestFactory" class is a class factory to create the HttpWebRequest object that sends cookies when a REST service call is made. If you want to know more on sending cookies while making a service call, you can take a look at this article.
  • The "ISerializerAdapter" is an interface to wrap the serializers. If you want to use the serializers of your choice when making a service call, you need to implement this interface to wrap your desired serializers. The "JavaScriptSerializerAdapter" wraps the JavaScriptSerializer. It will be the default serializer used by the "LightCase" client library.
  • The "ClientConfiguration" class will be used to configure how a service call is made.
  • The "RestCallBack" and "RestCallBackNonQuery" delegates are used to make asynchronous service calls.

The LightCase Generic Client Proxies

The "GenericProxies" class is the proxy class that we will be using to make REST service calls. It is implemented in three partial classes. The "GenericProxies.cs" file initiates the default behavior of the proxy class and implements some utility methods.

using System.IO;
using System.Net;
using System.Text;
using LightCaseClient.Support;
    
namespace LightCaseClient
{
    public static partial class GenericProxies
    {
        public static ClientConfiguration DefaultConfiguration { get; set; }
    
        // static Constructtor
        static GenericProxies()
        {
            // Initiate the default configuration
            DefaultConfiguration = new ClientConfiguration();
            DefaultConfiguration.ContentType = "application/json";
            DefaultConfiguration.Accept = "application/json";
            DefaultConfiguration.RequrieSession = false;
            DefaultConfiguration.OutBoundSerializerAdapter = new JavaScriptSerializerAdapter();
            DefaultConfiguration.InBoundSerializerAdapter = new JavaScriptSerializerAdapter();
        }
    
        // Create a request object according to the configuration
        private static HttpWebRequest CreateRequest(string url,
            ClientConfiguration clientConfig)
        {
            return (clientConfig.RequrieSession) ? CookiedHttpWebRequestFactory.Create(url) :
                (HttpWebRequest)WebRequest.Create(url);
        }
    
        // Post data to the service
        private static void PostData<T>(HttpWebRequest request,
            ClientConfiguration clientConfig, T data)
        {
            var jsonRequestString = clientConfig.OutBoundSerializerAdapter.Serialize(data);
            var bytes = Encoding.UTF8.GetBytes(jsonRequestString);
    
            using (var postStream = request.GetRequestStream())
            {
                postStream.Write(bytes, 0, bytes.Length);
            }
        }
    
        // Receive data from the service
        private static T ReceiveData<T>(HttpWebRequest request,
            ClientConfiguration clientConfig)
        {
            string jsonResponseString;
            using (var response = (HttpWebResponse)request.GetResponse())
            {
                var stream = response.GetResponseStream();
                if (stream == null) { return default(T); }
                using (var streamReader = new StreamReader(stream))
                {
                    jsonResponseString = streamReader.ReadToEnd();
                }
            }
    
            return clientConfig.InBoundSerializerAdapter.Deserialize<T>(jsonResponseString);
        }
    }
}
  • The static constructor "GenericProxies" initiates the default behavior of the REST service calls. The "ContentType" property is the data format to be sent to the service, the "Accept" property is the data format expected from the service by the client. The "RequrieSession" property defines if cookies will be sent to the service when the call is made. The "OutBoundSerializerAdapter" and "InBoundSerializerAdapter" defines the serializers used to serialize the data.
  • The "PostData" and "ReceiveData" methods are two utility methods that will be used later to simplify the coding of the proxy methods.

The proxy methods to support the synchronous REST service calls are implemented in the "GenericProxies.synchronous.cs" file:

using LightCaseClient.Support;
    
namespace LightCaseClient
{
    // Synchronous proxy implementation
    public static partial class GenericProxies
    {
        // ********************** Synchronous GET *************************
        public static TR RestGet<TR>(string url, ClientConfiguration configuration)
        {
            var clientConfig = configuration ?? DefaultConfiguration;
            var request = CreateRequest(url, clientConfig);
            request.Accept = clientConfig.Accept;
            request.Method = "GET";
    
            return ReceiveData<TR>(request, clientConfig);
        }
    
        // Overload method
        public static TR RestGet<TR>(string url)
        {
            return RestGet<TR>(url, DefaultConfiguration);
        }
    
    
        // ******** Synchronous GET, no response expected *******
        public static void RestGetNonQuery(string url,
            ClientConfiguration configuration)
        {
            var clientConfig = configuration ?? DefaultConfiguration;
            var request = CreateRequest(url, clientConfig);
            request.Method = "GET";
    
            request.GetResponse().Close();
        }
    
        // Overload method
        public static void RestGetNonQuery(string url)
        {
            RestGetNonQuery(url, DefaultConfiguration);
        }
    
        // ***************** Synchronous POST ********************
        public static TR RestPost<TR, TI>(string url,
            TI data, ClientConfiguration configuration)
        {
            var clientConfig = configuration ?? DefaultConfiguration;
            var request = CreateRequest(url, clientConfig);
            request.ContentType = clientConfig.ContentType;
            request.Accept = clientConfig.Accept;
            request.Method = "POST";
    
            PostData(request, clientConfig, data);
            return ReceiveData<TR>(request, clientConfig);
        }
    
        // Overload method
        public static TR RestPost<TR, TI>(string url, TI data)
        {
            return RestPost<TR, TI>(url, data, DefaultConfiguration);
        }
    
        // ****** Synchronous GET, no respons expected ******
        public static void RestPostNonQuery<TI>(string url,
            TI data, ClientConfiguration configuration)
        {
            var clientConfig = configuration ?? DefaultConfiguration;
            var request = CreateRequest(url, clientConfig);
            request.ContentType = clientConfig.ContentType;
            request.Accept = clientConfig.Accept;
            request.Method = "POST";
    
            PostData(request, clientConfig, data);
            request.GetResponse().Close();
        } 
    
        // Overload method
        public static void RestPostNonQuery<TI>(string url, TI data)
        {
            RestPostNonQuery(url, data, DefaultConfiguration);
        }
    }
}
  • The "RestGet" and "RestGetNonQuery" method will make a GET call to the REST service. If you only send data to the service, but do not expect a response from the service, you should use the "RestGetNonQuery" method.
  • The "RestPost" and "RestPostNonQuery" methods will make a POST call to the REST service. If you only send data to the service, but do not expect a response from the service, you should use the "RestPostNonQuery" method.
  • Each method has an overload. If the configuration object is not provided to the method when a call is made, the default configuration will be used.

Built upon the synchronous methods, the corresponding asynchronous methods are implemented in the "GenericProxies.asynchronous.cs" file:

using System;
using System.Runtime.Remoting.Messaging;
using LightCaseClient.Support;
    
namespace LightCaseClient
{
    // Asynchronous proxy implementation
    public static partial class GenericProxies
    {
        private delegate TR GetDelegate<TR>(string url,
            ClientConfiguration configuration);
        private delegate void GetNonQueryDelegate(string url,
            ClientConfiguration configuration);
        private delegate TR PostDelegate<TR, TI>(string url, TI data,
            ClientConfiguration configuration);
        private delegate void PostNonQueryDelegate<TI>(string url, TI data,
            ClientConfiguration configuration);
    
        // *************** Asynchronous Get ***************************
        public static void RestGetAsync<TR>(string url, RestCallBack<TR> callback,
            ClientConfiguration configuration)
        {
            var get = new GetDelegate<TR>(RestGet<TR>);
            get.BeginInvoke(url, configuration,
            ar =>
            {
                var result = (AsyncResult)ar;
                var del = (GetDelegate<TR>)result.AsyncDelegate;
                var value = default(TR);
                Exception e = null;
    
                try { value = del.EndInvoke(result); }
                catch (Exception ex) { e = ex; }
    
                if (callback != null) { callback(e, value); }
    
            }, null);
        }
    
        // Overload method
        public static void RestGetAsync<TR>(string url, RestCallBack<TR> callback)
        {
            RestGetAsync<TR>(url, callback, DefaultConfiguration);
        }
    
        // *********** Asynchronous Get, no response expected *************
        public static void RestGetNonQueryAsync(string url,
            RestCallBackNonQuery callback, ClientConfiguration configuration)
        {
            var get = new GetNonQueryDelegate(RestGetNonQuery);
            get.BeginInvoke(url, configuration,
            ar =>
            {
                var result = (AsyncResult)ar;
                var del = (GetNonQueryDelegate)result.AsyncDelegate;
                Exception e = null;
    
                try { del.EndInvoke(result); }
                catch (Exception ex) { e = ex; }
    
                if (callback != null) { callback(e); }
    
            }, null);    
    
        }
    
        // Overload method
        public static void RestGetNonQueryAsync(string url,
            RestCallBackNonQuery callback)
        {
            RestGetNonQueryAsync(url, callback, DefaultConfiguration);
        }
    
        // *************** Asynchronous Post *********************
        public static void RestPostAsync<TR, TI>(string url, TI data,
            RestCallBack<TR> callback, ClientConfiguration configuration)
        {
            var post = new PostDelegate<TR, TI>(RestPost<TR, TI>);
            post.BeginInvoke(url, data, configuration,
            ar =>
            {
                var result = (AsyncResult)ar;
                var del = (PostDelegate<TR, TI>)result.AsyncDelegate;
                var value = default(TR);
                Exception e = null;
    
                try { value = del.EndInvoke(result); }
                catch (Exception ex) { e = ex; }
    
                if (callback != null) { callback(e, value); }
    
            }, null);
        }
    
        // Overload method
        public static void RestPostAsync<TR, TI>(string url, TI data,
            RestCallBack<TR> callback)
        {
            RestPostAsync<TR, TI>(url, data, callback, DefaultConfiguration);
        }
    
    
        // ********* Asynchronous Post, not response expected *********
        public static void RestPostNonQueryAsync<TI>(string url, TI data,
            RestCallBackNonQuery callback, ClientConfiguration configuration)
        {
            var post = new PostNonQueryDelegate<TI>(RestPostNonQuery);
            post.BeginInvoke(url, data, configuration,
            ar =>
            {
                var result = (AsyncResult)ar;
                var del = (PostNonQueryDelegate<TI>)result.AsyncDelegate;
                Exception e = null;
    
                try { del.EndInvoke(result); }
                catch (Exception ex) { e = ex; }
    
                if (callback != null) { callback(e); }
    
            }, null);
        }
    
        // Overload method
        public static void RestPostNonQueryAsync<TI>(string url, TI data,
            RestCallBackNonQuery callback)
        {
            RestPostNonQueryAsync(url, data, callback, DefaultConfiguration);
        }
    }
}
  • The "RestGetAsync" and "RestGetNonQueryAsync" method will make a GET call to the REST service. If you only send data to the service, but do not expect a response from the service, you should use the "RestGetNonQueryAsync" method.
  • The "RestPostAsync" and "RestPostNonQueryAsync" methods will make a POST call to the REST service. If you only send data to the service, but do not expect a response from the service, you should use the "RestPostNonQueryAsync" method.
  • Each method has an overload. If the configuration object is not provided to the method when a call is made, the default configuration will be used.

When calling any one of these methods, you will need to provide a callback function. If you expect to receive data from the service, you need to create your callback function to match the "RestCallBack" delegate. Otherwise you need to create your callback function to match the "RestCallBackNonQuery" delegate. According to this article, these asynchronous methods will not throw exceptions if you compile your code in release mode. If an exception is encountered during the service call, it will be passed as a parameter into the callback function and you can process it accordingly. If no exception is encountered, null will be passed to this parameter. If you run your application in Visual Studio in debug mode, you may see your application breaks at the exception depending on how you set your Visual Studio environment.

How About the Other Http Methods?

You may have noticed that the "LightCase" Client library only supports GET and POST methods. Should I add the support to other methods too? In theory I should, but in practice, the GET and POST methods can meet any practical requirements to exchange data between the service and the client. According to my experience with the network engineers, they normally strongly go against the other methods such as PUT and DELETE. These methods are not considered as safe methods. We might argue that we can make our REST service safe even when these methods are used, but we cannot guarantee that other people also make their services safe with these methods. In many corporations, these methods are completely cut at the corporate firewall. So we have only the "GET" and "POST" options. If you find that you will absolutely need the support to the other Http methods, you can simply extend this library to support them yourself.

Now we complete the "LightCase" REST service client proxy library, we can take a look at an example on how to use it.

The Example Application

ExampleSolution.jpg

To demonstrate how to use the "LightCase" client library, I created a small example application. The attached Visual Studio 2010 solution has three projects. This example will only show you how to make a POST call with the "LightCase" Library. A GET call is simpler and it can be made similarly.

  • The "ShareLibraries" project defines a class used by both the service and the client applications;
  • The "Service" project implements a simple REST service;
  • The "Client" project implements the client of the service.

The "Student.cs" file in the "ShareLibraries" defines a simple class:

using System;
    
namespace ShareLibraries
{
    public class Student
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int Score { get; set; }
        public DateTime EvaluationTime { get; set; }
    }
} 

The "ShareLibraries" project will be referenced by both the "Service" and "Client" projects and the "Student" class will be used by both the service and the client to exchange data.

The REST Service

ExampleSolutionService.jpg

There are a couple of ways to implement a REST service. In this example, I will use my favorite method that I learned from here. If you want to see more discussions on the different ways to create a REST service, you can take a look at this article. The "Service" project is a simple ASP.Net project. Instead of building ASP.Net pages, I will use it to build a REST service. The service is implemented in the "StudentService.cs" file:

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Threading;
using ShareLibraries;
    
namespace Service
{
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode
        = AspNetCompatibilityRequirementsMode.Allowed)]
    public class StudentService
    {
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = "StudentService/EvaluateStudents")]
        public List<Student> EvaluateStudents(List<Student> students)
        {
            var rand = new Random();
            foreach (var student in students)
            {
                student.Score = 60 + (int)(rand.NextDouble() * 40);
                student.EvaluationTime = DateTime.Now;
            }
    
            Thread.Sleep(3 * 1000);
            return students;
        }
    }
} 
  • The OperationContract "EvaluateStudents" receives a list of "Student" objects from the client and assigns each student a random score.
  • In order to demonstrate the difference between a synchronous call and an asynchronous call, an artificial delay of three seconds is added.

I will be using this service to show you how to use the "LightCase" client library. In order for this service to work, we need to add the following mapping in the "Global.asax" file:

void Application_Start(object sender, EventArgs e)
{
    RouteTable.Routes.Add(new ServiceRoute("",
                new WebServiceHostFactory(),
                typeof(StudentService)));
} 

We also need to add the following configuration in the "Web.config" file:

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
      multipleSiteBindingsEnabled="true" />
    <standardEndpoints>
      <webHttpEndpoint>
        <standardEndpoint name="" helpEnabled="true"
          automaticFormatSelectionEnabled="true" />
      </webHttpEndpoint>
    </standardEndpoints>
</system.serviceModel> 

If we set the "" project as the start up project in the Visual Studio and launch it, we will see the following helper page if we type in the URL "http://localhost:62152/help" in the browser:

ServiceHelperPage.jpg

This helper page shows us the URL to access the "EvaluateStudents" REST method. It also shows us that the method to access the "EvaluateStudents" method is POST. As simple as it is, we have created a fully functional REST service. Let us now write a client using the "LightCase" client to consume this service.

The REST Service Client

ExampleSolutionClient.jpg

The "Client" project is a Windows Forms application. To use the "LightCase" client library, this application references the DLL created by the "LightCaseClient" project. It also references some .Net assemblies referenced by the "LightCaseClient" DLL.

For simplicity, this application has only one form implemented in the "frmMain.cs" file. The following is the design view of this Windows Form:

ClientDesignView.jpg

  • There are three buttons. One button will trigger a synchronous call to the service. The other will trigger an asynchronous call to the service. The "Click Me Anyway" button simply pops up a message box. I will use this button to show you that the UI is blocked by a synchronous call, but not blocked by an asynchronous call.
  • The DataGridView will show the list of the students after the scores assigned by the REST service.

The code-behind file of this Windows Form is the following:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Windows.Forms;
using LightCaseClient;
using ShareLibraries;
    
namespace Client
{
    public partial class frmMain : Form
    {
        private List<Student> students;
        private string serviceUrl;
    
        public frmMain()
        {
            InitializeComponent();
    
            // Get the service url
            serviceUrl = ConfigurationManager.AppSettings["ServiceUrl"];
    
            // Initialize a list of students
            students = new List<Student>();
            for(var i = 1; i <= 10; i++)
            {
                var student = new Student()
                { ID = i, Name = "Student Name No." + i.ToString() };
    
                students.Add(student);
            }
        }
    
        private void btnSynchronousCall_Click(object sender, EventArgs e)
        {
            dgStudents.DataSource = null;
            dgStudents.Refresh();
    
            // Make a synchronous POST Rest service call
            try
            {
                var evaluatedStudent = GenericProxies
                    .RestPost<List<Student>, List<Student>>(serviceUrl, students);
                dgStudents.DataSource = evaluatedStudent;
                dgStudents.Refresh();
            }
            catch (Exception ex)
            { MessageBox.Show("Failed to call the service - " + ex.Message); }
        }
    
        private void btnAsynchronousCall_Click(object sender, EventArgs e)
        {
            dgStudents.DataSource = null;
            dgStudents.Refresh();    
    
            // Make an asynchronous POST Rest service call
            GenericProxies.RestPostAsync<List<Student>, List<Student>>(serviceUrl, students,
                (ex, evaluatedStudent) =>
                    {
                        if (ex != null)
                            MessageBox.Show("Failed to call the service - " + ex.Message);
                        else
                            dgStudents.Invoke((Action) delegate
                            { dgStudents.DataSource = evaluatedStudent; dgStudents.Refresh(); });
                    }
             );
        }
    
    
        private void btnClickAnyway_Click(object sender, EventArgs e)
        {
            MessageBox.Show("I am clicked!");
        }
    }
}
  • In the constructor "frmMain", a list of "Student" objects is created.
  • In the "btnSynchronousCall_Click" method, a synchronous call is made to the REST service. The list of the students is POSTed to the service for evaluation. When the service call completes, the students with the scores are shown in the DataGridView.
  • In the "btnAsynchronousCall_Click" method, the same service call is made with the asynchronous API. The evaluated students are assigned to the DataGridView in the callback function. For simplicity, I used a Lambda expression to implement the callback function. When making the asynchronous call to the service using the "LightCase" client, I did not use exception handling. If any exception occurs during the call, the exception object will be passed to the Lambda callback function as a parameter. Otherwise, this parameter will be null.
  • The "btnClickAnyway_Click" method simply pops up a message box. I will use this message box to show you that the UI is blocked by the synchronous call, but not blocked by the asynchronous call.

If you take a look at my earlier article, you can find that it took me quite a few duplicate steps to complete the REST calls. But in the above example, the REST calls become simple function calls by using the "LightCase" client library. The URL to access the REST method is kept in the "App.config" file:

<appSettings>
    <add key="ServiceUrl" value="http://localhost:62152/StudentService/EvaluateStudents"/>
</appSettings>

Run the Example Application

RunApplication.jpg

If you set the "Client" project as the start up project, you can debug run the example application in the Visual Studio. If you click on either of the "Synchronous Call" and the "Asynchronous Call" buttons, the DataGridView will be populated with the data from the service after the artificial three seconds delay. If you click the "Click Me Anyway" button before the service call completes, you will notice that the UI is blocked by the synchronous call, but is not blocked by the asynchronous call.

Points of Interest

  • This article presented a generic REST service client library and an example on how to use it.
  • The "LightCase" client library only supports GET and POST methods, which should meet most if the practical requirements. But if you feel that you will need to use other methods, you can simply extend this library to add the support to them.
  • The "LightCase" client library is "virtually" thread safe. As long as you do not make changes to the configuration object when a service call is going on and I do not see a reason why you want to do it, you will not have thread safety issues.
  • If an asynchronous call is made and an exception occurs, the exception object will be passed to your callback function as a parameter. You need to check this object and process it accordingly.
  • I hope you like my postings and I hope this article can help you one way or the other.

History

First Revision - 2/19/2012.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Dr. Song Li

United States United States
I have been working in the IT industry for some time. It is still exciting and I am still learning. I am a happy and honest person, and I want to be your friend.

You may also be interested in...

Comments and Discussions

 
QuestionREST and WADL PinmemberMember 1015812517-Jul-13 3:25 
AnswerRe: REST and WADL PinmemberDr. Song Li17-Jul-13 3:49 
GeneralMy vote of 5 PinmemberYves Vaillancourt14-Mar-12 1:40 
Thanks! Very good article!
SuggestionClient security certificate PinmemberDipak V Bava8-Mar-12 10:01 
GeneralRe: Client security certificate PinmvpDr. Song Li8-Mar-12 10:32 
QuestionNice, very nice PinmvpSacha Barber19-Feb-12 22:47 
AnswerRe: Nice, very nice PinmvpDr. Song Li20-Feb-12 3:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 20 Feb 2012
Article Copyright 2012 by Dr. Song Li
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid