Consume WCF REST Service using jQuery


In a previous post I explained how to create a simple WCF Service in SharePoint.

https://kamilmka.wordpress.com/2012/08/24/restful-wcf-service-in-sharepoint/

In this post I’m going to explain how to consume the RESTfull WCF service using jQuery Ajax calls and interpret the JSON result.

WCF Services hosted in SharePoint does not support cross domain access by default. So in this example I’m going to develop a SharePoint Visual web part to consume the rest service. If you have a cross domain supporting service you can create a standalone app or a control using the same jQuery Ajax methods explained below.

1) Crate a Empty SharePoint Project.

2) Add a Visual web part to the project.

3) Make the jQuery script reference to the web part.

4) Add the below code to the user control file and deploy the solution. Add the web part to a web page and you can see the results.

  1.     <script type=”text/javascript”>
  2.         function GetLocationData() {
  3.             $.ajax({
  4.                 url: “/_vti_bin/Kamil.POC.WCFService/Service.svc/GetAllLocations”,
  5.                 success: function (result) {
  6.                     console.log(‘success’);
  7.                     PopulateMapLocations(result);
  8.                 },
  9.                 error: function (msg) {
  10.                     console.log(‘error’);
  11.                     console.log(msg);
  12.                     locationResults = null;
  13.                 }
  14.             });
  15.         }
  16.         function PopulateMapLocations(locationData) {
  17.             $.each(locationData, function () {
  18.                 console.log($(this)[0]);
  19.                 $(‘#locationResultsPlaceHolder’).append($(this)[0].Title);
  20.                 $(‘#locationResultsPlaceHolder’).append(‘<br>’);
  21.                 $(‘#locationResultsPlaceHolder’).append($(this)[0].Address);
  22.                 $(‘#locationResultsPlaceHolder’).append(‘<br><br>’);
  23.             });
  24.         }
  25.         $(document).ready(function () {
  26.             GetLocationData();
  27.         });
  28.     </script>

This script has a function named GetLocationData that will be calling the RESTfull WCF service. So from above you can see how simple to call the jQuery Ajax function to call the service. Only must provide option is url and in my sample I have provided the REST url for the GetLocations method as defined in WCF service. If service call get succeeded then I’m calling the PopulateMapLocations method with passing the result data. The result data is a JSON value location value collection. So we can iterate through the collection and retrieve any attribute same as the way we defined in the WCF service.

image
(Result view in browser)

In several places you can see I have used console.log() function. This is a very user friendly effective way of debugging scripts. I used to use alerts in the middle of the script but its very unfriendly when you are iterating through a loop. But this console.log option will log the passed parameter or the object to the browser console. And if you are using Google Chrome you can expand the object and see the result as well. So something very useful when developing and testing.

image
(Screen cap on how the Chrome browser console looks as per this sample)

Will have another post on how to display these consumed data in a Bing Map. Until then Happy Coding! Smile

Advertisements

Using Twitter API v1.1 with .net (oAuth) – no 3rd party libraries


Due March 2013 Twitter API V1.0 get deprecated and all requested to use V 1.1 instead. I had a Twitter web part created for a public facing site and I had to update it to this new API version. Most of the articles i read including Twitter it says its pretty straight forward. But I had to go through a very hard time figuring out how to connect to API using oAuth 1.0 and how to pass additional parameters. There are misty hidden behind the API calls and signing signatures that not described in detail in any documentation.This attempt is to clarify the procedure as much as possible and give you a working version of a implementation.
Code sample given below is generalized and you can pass any twitter parameter related to user time line related query. Just add the parameters to additional_parameters section.

If you had already started trying to implement this you may have received some common error message while testing it.

The remote server returned an error: (400) Bad Request.

The remote server returned an error: (401) Unauthorized.

Some Key reasons to get those error messages is not relevant directly to the error message it self.

1) Base string used to create signature should have there parameters alphabetically sorted. (thank you Priyantha Sampath Lankapura for pointing this out to me Smile )

2) What ever the parameters included in the signature must present in the request url sent to twitter. You cannot change a single parameter value. Authenticity of the request is validated through the URL and the signature sent in the header.

(The complete list of error messages can be found here.

https://dev.twitter.com/docs/error-codes-responses)

Please note all the tokens provided below are incorrect and you need to replace them with your own application tokens you created to get this sample work. If you don’t have these token already created you can do so from twitter developer site.

https://dev.twitter.com/docs/auth/tokens-devtwittercom

 

Code Sample
  1.          
  2.          try
  3.             {
  4.                 CultureInfo _culture = new CultureInfo(“en-US”);
  5.                 #region oAuth realted parameters
  6.                 var oauth_token = “176122-SQA1amiFz1Q1cX7ZRRzyJsbyWfnwb5zQtOsZY”;
  7.                 var oauth_token_secret = “2ZhjEPv4bP5ojpj6hPSLFEmW2nAsYzIrEkhL5YJs”;
  8.                 var oauth_consumer_key = “2rYXfK6o76tz2uwIUJ9A”;
  9.                 var oauth_consumer_secret = “38TDM2qD1Fi5M51yFxljAC9dX5TQD1uusAyUEVw”;
  10.                 var resource_url = https://api.twitter.com/1.1/statuses/user_timeline.json&#8221;;
  11.                 var oauth_version = “1.0”;
  12.                 var oauth_signature_method = “HMAC-SHA1”;
  13.                 var oauth_nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString(CultureInfo.InvariantCulture)));
  14.                 var timeSpan = DateTime.UtcNow – new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
  15.                 var oauth_timestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString(CultureInfo.InvariantCulture);
  16.                 SortedDictionary<string, string> oauth_parameters = new SortedDictionary<string, string>();
  17.                 oauth_parameters.Add(“oauth_consumer_key”, oauth_consumer_key);
  18.                 oauth_parameters.Add(“oauth_nonce”, oauth_nonce);
  19.                 oauth_parameters.Add(“oauth_signature_method”, oauth_signature_method);
  20.                 oauth_parameters.Add(“oauth_timestamp”, oauth_timestamp);
  21.                 oauth_parameters.Add(“oauth_token”, oauth_token);
  22.                 oauth_parameters.Add(“oauth_version”, oauth_version);
  23.                 #endregion oAuth realted parameters
  24.                 #region Twitter time line related additonal parameters
  25.                 // Some variable parameters
  26.                 var screen_name = “twitterapi”;
  27.                 int count = 5;
  28.                 SortedDictionary<string, string> additional_parameters = new SortedDictionary<string, string>();
  29.                 additional_parameters.Add(“screen_name”, Uri.EscapeDataString(screen_name));
  30.                 additional_parameters.Add(“count”, count.ToString());
  31.                 additional_parameters.Add(“exclude_replies”, “false”);
  32.                 additional_parameters.Add(“include_rts”, “true”);
  33.                 additional_parameters.Add(“trim_user”, “true”);
  34.                 additional_parameters.Add(“contributor_details”, “true”);
  35.                 #endregion Twitter time line related additonal parameters
  36.                 #region Merge all parameters together and sort them
  37.                 SortedDictionary<string, string> mergerd_parameters = new SortedDictionary<string, string>();
  38.                 foreach (var item in oauth_parameters)
  39.                 {
  40.                     mergerd_parameters.Add(item.Key, item.Value);
  41.                 }
  42.                 foreach (var item in additional_parameters)
  43.                 {
  44.                     mergerd_parameters.Add(item.Key, item.Value);
  45.                 }
  46.                 #endregion Merge all parameters together and sort them
  47.                 #region Generate the base string for sigining
  48.                 var base_string = string.Empty;
  49.                 foreach (var item in mergerd_parameters)
  50.                 {
  51.                     base_string += item.Key + “=” + item.Value + “&”;
  52.                 }
  53.                 base_string = base_string.Remove(base_string.Length – 1);
  54.                 base_string = string.Concat(“GET&”, Uri.EscapeDataString(resource_url),
  55.                               “&”, Uri.EscapeDataString(base_string));
  56.                 #endregion Generate the base string for sigining
  57.                 #region Generate signature
  58.                 //Encrypt data
  59.                 var composite_key = string.Concat(Uri.EscapeDataString(oauth_consumer_secret),
  60.                             “&”, Uri.EscapeDataString(oauth_token_secret));
  61.                 string oauth_signature;
  62.                 using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(composite_key)))
  63.                 {
  64.                     oauth_signature = Convert.ToBase64String(hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes(base_string)));
  65.                 }
  66.                 #endregion Generate signature
  67.                 #region Prepare authentication header and the request url
  68.                 var header_format = “OAuth oauth_nonce=\”{0}\”, oauth_signature_method=\”{1}\”, “ +
  69.                        “oauth_timestamp=\”{2}\”, oauth_consumer_key=\”{3}\”, “ +
  70.                        “oauth_token=\”{4}\”, oauth_signature=\”{5}\”, “ +
  71.                        “oauth_version=\”{6}\””;
  72.                 var auth_header = string.Format(_culture, header_format,
  73.                                         Uri.EscapeDataString(oauth_nonce),
  74.                                         Uri.EscapeDataString(oauth_signature_method),
  75.                                         Uri.EscapeDataString(oauth_timestamp),
  76.                                         Uri.EscapeDataString(oauth_consumer_key),
  77.                                         Uri.EscapeDataString(oauth_token),
  78.                                         Uri.EscapeDataString(oauth_signature),
  79.                                         Uri.EscapeDataString(oauth_version)
  80.                                 );
  81.                 //Disable exprect 100 continue header
  82.                 ServicePointManager.Expect100Continue = false;
  83.                 string resource_url_with_param = resource_url + “?”;
  84.                 foreach (var item in additional_parameters)
  85.                 {
  86.                     resource_url_with_param += item.Key + “=” + item.Value + “&”;
  87.                 }
  88.                 resource_url_with_param = resource_url_with_param.Remove(resource_url_with_param.Length – 1);
  89.                 #endregion Prepare authentication header and the request url
  90.                 #region Request Twitter API and process results
  91.                 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(resource_url_with_param);
  92.                 request.Headers.Add(“Authorization”, auth_header);
  93.                 request.Method = “GET”;
  94.                 request.ContentType = “application/x-www-form-urlencoded”;
  95.                 WebResponse response = request.GetResponse();
  96.                 string response_data;
  97.                 using (StreamReader reader = new StreamReader(response.GetResponseStream()))
  98.                 {
  99.                     response_data = reader.ReadToEnd();
  100.                     DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(List<Tweet>));
  101.                     using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(response_data)))
  102.                     {
  103.                         List<Tweet> tweet_collection = ser.ReadObject(ms) as List<Tweet>;
  104.                         foreach (var tweet in tweet_collection)
  105.                         {
  106.                                 //Do something here
  107.                         }
  108.                     }
  109.                 }
  110.                 #endregion Request Twitter API and process results
  111.             }
  112.             catch (Exception ex)
  113.             {
  114.                 throw ex;
  115.             }
  116.             

Twitter Entity
  1.         [DataContract]
  2.         public class Tweet
  3.         {
  4.             [DataMember]
  5.             public long id;
  6.             [DataMember]
  7.             public string id_str;
  8.             [DataMember]
  9.             public string text;
  10.             [DataMember]
  11.             public string created_at;
  12.         }

Note on the above sample:

oauth_parameters are the must provide parameters to sign the signature and connect to the API.
additional_parameters section includes all the parameters you need to customize the result set you gonna retrieve.

I have used a custom entity named Twitter to store the data retrieved from the json result object. This entity does not include all the result data. So you can add more fields as defined in the twitter entity documentation to read those values to the entity.

Happy Coding! Smile

Using Bing Map API V7


This article is intended to who is new to Bing map API. This article will guide to create a simple Bing map with basic configurations that we will be using most of the time.

First of all if you need to get a Bing Map API key for your development tasks you can do it very simply and for free using the Microsoft Bing Map Portal.
http://www.bingmapsportal.com/

But most of operations and developments can be done even without using a valid Bing Map API key. But the map will have a huge water mark in the center of the map and it will make your map unusable. So its worth for you to get the free API key. Winking smile

The complete source can be found here and the explanations goes under the code sample.

  1.     <style>
  2.         #bingMapPlaceHolder
  3.         {
  4.             width: 600px;
  5.             height: 600px;
  6.             position: relative;
  7.             float: left;
  8.         }
  9.     </style>
  10.     <script type=”text/javascript” src=”/Style Library/Scripts/jquery-1.9.1.js”></script>
  11.     <script type=”text/javascript” src=”https://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&s=1″></script>
  12.     <script language=”javascript” type=”text/javascript”>
  13.         var map;
  14.         var bingcredentials = ‘YourBingMapKey’;
  15.         var mapCenterLatitude = ‘6.931944’;
  16.         var mapCenterLongitude = ‘79.847778’;
  17.         var mapZoomLevel = ‘10.8’;
  18.         // Initialize the Map instance
  19.         function InitializeMap() {
  20.             try {
  21.                 Microsoft.Maps.loadModule(‘Microsoft.Maps.Overlays.Style’,
  22.             {
  23.                 callback: function () {
  24.                     map = new Microsoft.Maps.Map(document.getElementById(“bingMapPlaceHolder”),
  25.                     {
  26.                         credentials: bingcredentials,
  27.                         mapTypeId: Microsoft.Maps.MapTypeId.road,
  28.                         enableSearchLogo: false,
  29.                         showCopyright: false,
  30.                         showLogo: false,
  31.                         showDashboard: true,
  32.                         customizeOverlays: true,
  33.                         center: new Microsoft.Maps.Location(eval(mapCenterLatitude), eval(mapCenterLongitude)),
  34.                         zoom: eval(mapZoomLevel)
  35.                     });
  36.                 }
  37.             });
  38.             } catch (e) {
  39.                 alert(e);
  40.             }
  41.         }
  42.         $(document).ready(function () {
  43.             InitializeMap();
  44.         });
  45.     </script>
  46.     <div id=”bingMapPlaceHolder”>
  47.     </div>

We do not need jQuery for this example. But in many scenarios our Bing Map will be hosted as a control inside SharePoint web part, asp.net control, or in a PHP page etc. So to increase the scalability and usability I have used jQuery document ready function to initialize and display the map.

First of all you need to define a placeholder element to display the map. In this example I have defined a div with the ID bingMapPlaceHolder for this purpose. Make sure you set the width and height of this placeholder. I have define some style for this top of the example.

In the script section you can see few variable. map is to hold the Bing map and the rest is supporting variables. As I mentioned earlier Bing map requires a valid API key to function properly and completely as expected. so the bingcredentials variable will hold my Bing map API key.

When we open up the map it will zoom to the entire world. So if we want to zoom and center the map to a specific point (city, state, country, address, geo codes etc.) we need to pass them when we initialize the map. So for that purpose I have few variables that will define the center location latitude, longitude and the default zoom level.

Then I have written a function InitializeMap to define map control and to set the necessary options. Line 29 to 40 does this set up. When initializing the map we will be specifying the html element we going to load the map.

Other options are explained below:

mapTypeId: Microsoft.Maps.MapTypeId.road
Bing map has different views such as Road view, Birds Eye, Areal etc. So this is to specify that I need the Road view.

enableSearchLogo: false
By Default a Bing Search option will be there inside the map it self. So I want to get rid of this.

showCopyright: false
By default there will be a copyright text at the bottom of the map. This option let you show hide it.

showLogo: false
If you don’t wont to show the Bing logo in your map you can set this value to false.

showDashboard: true
Dashboard is the place that will give you the control over map. View selection, Zoom In, Zoom out etc. are specified in the dashboard.

All the options you can apply to the map is documented in msdn can be found here.
http://msdn.microsoft.com/en-us/library/gg427603.aspx

If you inspect line 25 (Microsoft.Maps.loadModule(‘Microsoft.Maps.Overlays.Style’) you can see I have loaded a specific module. There are bunch of different modules provided and catering different needs. For example for traffic, clustering the pin locations, routing and directions etc.

In this example I have loaded the ‘Microsoft.Maps.Overlays.Style’ module because I want Microsoft Bing Map like navigation options in my dashboard. By default it gives a different look and feel. Likewise by loading different modules you can add more functionality and also change the look and feel too.

You can get an idea on default modules and custom modules by referencing these articles.
http://msdn.microsoft.com/en-us/library/hh125837.aspx
http://bingmapsv7modules.codeplex.com/

At at the bottom of the page, I have called the initialize method inside my document ready jQuery function. So this will load all the necessary stuff and will display the map.

If you want try out different simple and advanced options of Bing map you can simple navigate to below link of Bing Map SDK and try them out. This is the best place to see the capabilities of the map API.
http://www.bingmapsportal.com/isdk/ajaxv7

That’s it about loading a simple Bing map. Probably you will need to pin and show locations and search locations etc. Article explaining on those topics are to be followed. Smile

RESTful WCF Service in SharePoint


Recently I had to write a Custom WCF Service in SharePoint 2010 to expose some map locations related data. Whenever we think about web services or WCF services what first pops up into mind is to update web config files with different configurations to define end points and all. Usually it was difficult to get it work at once.

But you may be surprised when I say you no need even to have a web.config file when you write a WCF service that hosted or resides inside SharePoint Smile Sounds interesting right? So these are the basic steps you need to follow to get it working.

Code sample for this post can be found here.

1) Create an Empty SharePoint project.

image

2) Add following two assembly references to the project.
System.ServiceModel
System.ServiceModel.Web

image

3) Add a Mapped folder to ISAPI folder.

image

4) Create a sub folder inside the mapped ISAPI folder that will hold the WCF service (this is for the best practices so that your service can be easily identified after it get deployed).

5) Add a new text file to the newly created folder with the required service name. (Service.txt)

6) Change the text file extension to svc (Service.svc). After the steps 4, 5 and 6 it should be looking like this.

image

7) Add an interface file to the project. (IService.cs)

  1.     [ServiceContract]
  2.     interface IService
  3.     {
  4.         [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = “GetAllLocations”)]
  5.         [OperationContract]
  6.         List<MapLocation> GetAllLocations();
  7.     }

Decorate the Interface using the ServiceContract attribute. Then decorate the web method that you are exposing through the service with the OperationContract method level attribute. Additional WebGet attribute will be used to define what type of a response result you should be giving out from this service. It can be Json format or XML. UriTemplate portion define the how you are going to call this method externally. For example in the service call url if you append the GetAllLocations like “…service.svc/GetAllLocations” then this particular method will be called. You can give a friendly and more descriptive url there even the underlying method name is totally different.

In this example I’m returning a list of Location data with a custom entity collection.

8) Add a class file to the project to hold the implementation of the interface.

  1.     [AspNetCompatibilityRequirementsAttribute(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
  2.     internal class Service : IService
  3.     {
  4.         public List<MapLocation> GetAllLocations()
  5.         {
  6.             return MapLocation.All.ToList();
  7.         }
  8.     }

You need to add the AspNetCompatibilityRequirementsAttribute and set the value as Required or Allowed. If not you wont be able to get the result from this method call. Even the method call will be successful the result will be an error text something similar as below.

“The service cannot be activated because it does not support ASP.NET compatibility. ASP.NET compatibility is enabled for this application. Turn off ASP.NET compatibility mode in the web.config or add the AspNetCompatibilityRequirements attribute to the service type with RequirementsMode setting as ‘Allowed’ or ‘Required’”

9) Definition of the MapLocation entity and the implementation of the All property is below. Add a separate class file and copy the below implementation to it.

  1.     public class MapLocation
  2.     {
  3.         public static IEnumerable<MapLocation> All
  4.         {
  5.             get
  6.             {
  7.                 List<MapLocation> allLocations = new List<MapLocation>();
  8.                 allLocations.Add(new MapLocation() { ID = 1, Title = “Home”, Address = “No 2 Badulla Road Bandarawela”, Latitude = 8.319533, Longitude = 79.978409 });
  9.                 allLocations.Add(new MapLocation() { ID = 2, Title = “Rented House”, Address = “Attidiya Dehiwala”, Latitude = 8.392224, Longitude = 80.930786 });
  10.                 allLocations.Add(new MapLocation() { ID = 3, Title = “Shop”, Address = “Badulla Road Bandarawela”, Latitude = 8.5, Longitude = 81.930786 });
  11.                 allLocations.Add(new MapLocation() { ID = 4, Title = “My Office”, Address = “M&M Centre Rajagiriya”, Latitude = 7.560078, Longitude = 79.872429 });
  12.                 return allLocations;
  13.             }
  14.         }
  15.         public int ID { get; set; }
  16.         public string Title { get; set; }
  17.         public string Address { get; set; }
  18.         public double Latitude { get; set; }
  19.         public double Longitude { get; set; }
  20.     }

10) Final step of coding is to update the Service.svc file. So open up the file and add the below code in to it.

  1. <%@ ServiceHost Language=”C#” Debug=”true” Service=”Kamil.POC.WCFService.Service, Kamil.POC.WCFService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b87ecdf1ed188035″ Factory=”Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory, Microsoft.SharePoint.Client.ServerRuntime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>

In the above code Service attribute will define the implementation of your service. So please make sure you update the correct namespace and the assembly information. Factory attribute will define what type of service you going to expose. Is it a RESTful service or a SOAP service ect.

All the types you can specify in Factory attribute can be found below.

SOAP service – MultipleBaseAddressBasicHttpBindingServiceHostFactory
Basic HTTP binding must be used, which creates endpoints for a service based on the basic HTTP binding.

REST Service – MultipleBaseAddressWebServiceHostFactory
The service factory creates endpoints with Web bindings.

ADO.NET Data Service – MultipleBaseAddressDataServiceHostFactory
A data service host factory can be used.
So we are done with the coding and simple you can deploy the newly created WCF service to SharePoint using the deploy option in Visual Studio.
To test the service navigate to the below url in your browser.

http://<ServerName>:[Port]/_vti_bin/Kamil.POC.WCFService/Service.svc/GetAllLocations

This will return you a Json response. If you have any Json formatter extensions installed on your browser it will give you a nice hierarchical view of the result data.

image

In the next post I will explain how to create a simple web part and retrieve this data using jQuery Ajax methods.

Happy Coding! Smile