Simple HttpClient function to handle all Http Methods

With MVC 5 and WebApi, HttpClient provides a simple way to make API calls to another application. Here’s a sample function that can handle all Http methods.

HttpClient.SendAsync() method handles all http methods, a key part of this solution

 


private HttpResponseMessage CallHttpApi(HttpMethod method, 
	string url, 
	string jsonContent, 
	List<KeyValuePair<string,string>> headers = null)
{
	using (var client = new HttpClient())
	{
		client.DefaultRequestHeaders.Accept.Clear();
		client.DefaultRequestHeaders.Accept.
			Add(new MediaTypeWithQualityHeaderValue("application/json"));

		try
		{
			var request = new HttpRequestMessage()
			{
				RequestUri = new Uri(url),
				Method = method,
				Content = new StringContent(jsonContent, 
					Encoding.UTF8, "application/json"),
			};

			if (headers != null)
			{
				client.DefaultRequestHeaders.AsQueryable()
					.ToForEach(a => { request.Headers.Add(a.Key, a.Value); });
				headers.ForEach(a=>{request.Headers.Add(a.Key, a.Value);});
			}

			var resp = client.SendAsync(request).Result;
			resp.EnsureSuccessStatusCode();
			return resp;
		}
		catch (Exception ex)
		{
			var exRes = ex;
			if (ex is AggregateException)
			{
				exRes = ((AggregateException)ex).Flatten();
			}
			throw exRes.HandleException();
		}
	
	}
}

public static void ToForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
	foreach (T item in enumeration)
	{
		action(item);
	}
}

public static partial class ExceptionManager
{
	public static Exception HandleException(this Exception ex)
	{
		return HandleException(ex, string.Empty);
	}

	public static Exception HandleException(this Exception ex, 
		string messageFormat, params object[] args)
	{
		return HandleException(ex, string.Format(messageFormat, args));
	}

	public static Exception HandleException(this Exception ex, string newMessage)
	{
		Exception nuEx = null;
		if (!string.IsNullOrEmpty(newMessage))
		{
			nuEx = new Exception(newMessage);
			nuEx.Data.Add(ex.Message, ex.StackTrace);
		}
		else
		{
			nuEx = ex;
			try
			{
				nuEx.Data.Add(nuEx.Message, nuEx.StackTrace);
			}
			catch { }
		}
		return nuEx;
	}
}

List<KeyValuePair<string, string>> h = new List<KeyValuePair<string, string>>();
h.Add(new KeyValuePair<string,string>("X-CUSTOM-USERID", "WHATEVER"));
h.Add(new KeyValuePair<string,string>("X-CUSTOM-INFO", "AGAIN WHATEVER"));

var resp = CallHttpApi(HttpMethod.Post, url, 
	JsonConvert.SerializeObject( products), h);
Debug.WriteLine(resp.Content);

IE 8/9 CORS support in Angular.js

Many would agree with me that to create a website to run on IE is a headache chore. I would have dropped the IE browser if we were given that choice. Our team has decided to use Angular as our primary html platform. It works great and we all loved it, until the app started to break in IE, which is caused by lack of IE cors (cross-origin-resource-sharing) support. Angular had a solution to use XDomainRequest, but removed due to its limited capabilities. Fortunately, there’s a better solution. Let’s go over the steps:

First, we have to hijack the $http service. So that it can support IE CORS when detected. On the main application where Angular is expected to access the resources (templates/API) on another server, we will modify the angular.js file to use our very own httpBackendProvider. Find the $HttpBackendProvider function and replace it as below (roughly between 9200 to 9300 line, for v1.0.7)

function $HttpBackendProvider() {
  this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
      var params = [$browser, XHR, $browser.defer, $window.angular.callbacks, $document[0], $window.location.protocol.replace(':', '')]; /*orig xhr */
      var param4ie = params.concat([msie,createHttpBackend.apply(this,params)]);
      return (angular.ieCreateHttpBackend && angular.ieCreateHttpBackend.apply(this, param4ie)) ||
        createHttpBackend.apply(this, params);
  }];
}

2nd step is to create the ieCreateHttpBackend method and that’s where we put the override logic for IE CORS support. see the code below, we are extending angular namespace with the new function ieCreateHttpBackend. Lets save it as angular.ieCors.js, be sure to wrap it as a self invoking function.

window.angular = {
  ieCreateHttpBackend: function ($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol, msie, xhr) {
    if (!msie || msie > 9) return null;

    var getHostName = function (path) {
      var a = document.createElement('a');
      a.href = path;
      return a.hostname;
    }

    var isLocalCall = function (reqUrl) {
      var reqHost = getHostName(reqUrl),
        localHost = getHostName($browser.url());

      patt = new RegExp( localHost + "$", 'i'); 
      return patt.test(reqHost);
    }

    function completeRequest(callback, status, response, headersString) {
      var url = url || $browser.url(),
        URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?(\{?[\w\.-]*\}?)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/;


      // URL_MATCH is defined in src/service/location.js
      var protocol = (url.match(URL_MATCH) || ['', locationProtocol])[1];

      // fix status code for file protocol (it's always 0)
      status = (protocol == 'file') ? (response ? 200 : 404) : status;

      // normalize IE bug (http://bugs.jquery.com/ticket/1450)
      status = status == 1223 ? 204 : status;

      callback(status, response, headersString);
      $browser.$$completeOutstandingRequest(angular.noop);
    }
    var pmHandler = function (method, url, post, callback, headers, timeout, withCredentials) {
      var win =  $('[name="' + getHostName(url) + '"]')[0].id ;
      console.log('ie postMessage for url : ' + url);
      console.log( 'iframe window ' + win);
      pm({
        target: window.frames[win],
        type: 'xhrRequest',
        data: {
          headers: headers,
          method: method,
          data: post,
          url: url
        },
        success: function (respObj) {
          completeRequest(callback, 200, respObj.responseText, 'Content-Type: ' + respObj.contentType);
        },
        error: function (data) {
          completeRequest(callback, 500, 'Error', 'Content-Type: text/plain');
        }
      });
    }
    return function (method, url, post, callback, headers, timeout, withCredentials) {
      $browser.$$incOutstandingRequestCount();
      url = url || $browser.url();

      if (isLocalCall(url) ) {
        xhr(method, url, post, callback, headers, timeout, withCredentials);
      } else {
        pmHandler(method, url, post, callback, headers, timeout, withCredentials);
      }
      if (timeout > 0) {
        $browserDefer(function () {
          status = -1;
          xdr.abort();
        }, timeout);
      }
    }

  }
};

The entire IE CORS solution is base on postMessage.js , and it uses an invisible iframe to make XmlHttpRequest to another server. So we have to setup an iframe in the body

angular_ie_cors_iframe

As indicated above, we are expecting the remote server to receive the request via the ClientProxy page. We will have to setup the page later.

Next step is to make sure the js files are loaded properly. Due to the way how it is injected into Angular, be sure to load angular.ieCors.js prior to angular.js

<script src="/Scripts/jquery-1.10.2.js"></script>
<script src="/Scripts/postMessage.js"></script>
<script src="/Scripts/angular.ieCors.js"></script>
<script src="/Scripts/angular.js"></script>

Now let’s switch our focus to the ClientProxy page, which is hosted on the remote server where the resources are (html templates, api, etc). In order for CORS to work, the resource server must reply with the access-control origin headers. “*”, a wildcard origin, should be used with cautious. These headers basically tell client browser to enable CORS for the http requests.

Access-Control-Allow-Headers:Accept, Content-MD5, Content-Type, X-Requested-With
Access-Control-Allow-methods:POST, GET, OPTIONS, PUT, DELETE
Access-Control-Allow-Origin:*
Access-Control-Max-Age:1728000

Here’s the code for ClientProxy


<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="/Scripts/jquery-1.10.2.js"></script>
<script src="/Scripts/postMessage.js"></script>
<script type="text/javascript">
  (function () {
    pm.bind("xhrRequest", function (data) {
      console.log(JSON.stringify(data));
      
      if (data && data.url) {
        var ret = $.ajax({
          type: data.method,
          url: data.url,
          headers: data.headers,
          data: data.data,  //forgot to include the body
          async: false
        });
        return {
          responseText: ret.responseText,
          contentType: ret.getResponseHeader("Content-Type")
        };
      }
      return '';
    });

  })();

</script>
</head>
<body style="color:#3e3e3e">
  This page is intentionally left blank
</body>
</html>


That’s pretty much it. The final point I should make is that jQuery has stopped supporting IE8/9 with v2.0; it maybe good to stick with v1.10.2

Load javascript files dynamically with jQuery getScript and promise

In my current project, I need to load set of javascript files dynamically and sequentially. To my surprise that I can’t find a decent solution on the net. End up creating my own jquery plugin to load the javascript files.

The following sample code, scriptLoader, was built with jQuery getScript and promise.

 

(function ($) {
    jQuery.fn.extend({
        scriptLoader: function (scripts) {
                var chain = $.Deferred();
                chain.resolve();
                scripts.forEach(function (script, index, array) {
                    chain = chain.then(function () {
                        return $.getScript(script, function (data, status) {
                            console.info('Loaded: ' + status + ' - '  + script);
                        });
                    });
                });
                chain.then(function () {
                    console.info('scriptLoader completed');
                });
                return chain.promise();
        }
    });
})(jQuery);

 

Here’s an example on using the scriptLoader. It’s pretty simple and straightforward


<script >
  var scripts = [
    'Scripts/ui-bootstrap-tpls-0.4.0.js',
    'Scripts/controllers/controllers.MainController.js',
    'Scripts/controllers/controllers.PrettyController.js',
    'Scripts/services/services.dataService.js',
  ];

  $(document).scriptLoader(scripts)
    .then(function () {
      angular.element(document).ready(function () {
      angular.bootstrap($('#angularContainer'), ['angularApp']);
    });
  });
 </script>