Search This Blog

Monday, February 8, 2016

SharePoint 2013: Create a Custom WCF REST Service Hosted in SharePoint

RESTful services are those which follow the REST (Representational State Transfer) architectural style. Before implementing your first RESTful service, lets first understand the concept behind it. As we know that WCF allows us to make calls and exchange messages using SOAP over a variety of protocols i.e. HTTP, TCP, Named Pipes and MSMQ etc. In a scenario, if we are using SOAP over HTTP, we are just utilizing HTTP as a transport. But HTTP is much more than just a transport. So, when we talk about REST architectural style, it dictates that “Instead of using complex mechanisms like CORBA, RPC or SOAP for communication, simply HTTP should be used for making calls”.


WCF RESTful ServiceRESTful architecture use HTTP for all CRUD operations like (Read/Create/Update/Delete) using simple HTTP verbs like (GET, POST, PUT, and DELETE).It’s simple as well as lightweight. For the sake of simplicity, I am going to implement only a GET request for which service will return certain types of data (i.e. Product data) in XML format.

Following are 5 simple steps to create your first RESTful service that returns data in XML.
  • Create a WCF Service Project.
  • Preparing the data (e.g. Product) to return
  • Creating Service Contract
  • Implementing Service
  • Configure Service and Behavior
SharePoint 2013 provides a robust Representational State Transfer (REST) interface that allows any technology that supports standard REST capabilities to interact with SharePoint (sites, libraries, lists, etc).  In addition to the built-in SharePoint REST API, you can create your own custom Windows Communication Foundation (WCF) REST services that are hosted in SharePoint.  In this example, we'll explore the steps necessary to create a SharePoint-hosted WCF service with a REST interface that is deployed in a SharePoint solution (.wsp).
Download the source (50.8 KB)

Step-by-Step Instructions

  1. Run Visual Studio 2013 as an Administrator.
  2. Create a new project named Barkes.Services.Presidents using the SharePoint 2013 - Empty Project template from the Visual C#SharePoint Solutions category.
  3. Ensure the Deploy as farm solution option is selected.
  4. After the project is created, right-click on the project in the Solution Explorer, then Add -> SharePoint Mapped Folder.
  5. On the Add SharePoint Mapped Folder dialog, select the ISAPI folder and click OK.
  6. Right-click on the ISAPI folder in the Solution Explorer, then Add -> New Item.
  7. On the Add New Item dialog, select Text File from the General category and enter  PresidentsService.svc as the name, then clickAdd.  Make sure to change the default file extension from txt to svc.
     
  8. Right-click on the ISAPI folder in the Solution Explorer, then Add -> New Item.
  9. On the Add New Item dialog, select Code File from the Code category and enter PresidentsService.svc.cs as the name, then clickAdd.
  10. Right-click on the ISAPI folder in the Solution Explorer, then Add -> New Item.
  11. On the Add New Item dialog, select Interface from the Code category and enter IPresidentsService.cs as the name, then click Add.
  12. Add the required assembly references by right-clicking References, then Add Reference from the Solution Explorer.
  13. On the Reference Manager dialog, select Framework and check System.Runtime.SerializationSystem.ServiceModel andSystem.ServiceModel.Web, then click OK.
  14. By default Visual Studio does not support token replacements in .SVC files.  In order to use the$SharePoint.Project.AssemblyFullName$ token, right-click on the project in Solution Explorer, then Unload Project.  If you are prompted to save the project, select yes.
  15. Right-click the project in the Solution Explorer, then Edit Barkes.Services.Presidents.csproj.
  16. In the first PropertyGroup (toward the top of the project file), add the TokenReplacementFileExtensions element beneath the SandboxedSolution element and set its value to svc.  Don't forget to save the changes to the project file.
    <PropertyGroup>
        ...
        <SandboxedSolution>False</SandboxedSolution>
        <TokenReplacementFileExtensions>svc</TokenReplacementFileExtensions>
    </PropertyGroup>
  17. After you've made the required manual project changes, right-click the project and select Reload Project.
  18. Open PresidentsService.svc and enter the following service declaration.  Note that the use of the SharePoint-specificMultipleBaseAddressWebServiceHostFactory replaces the need to specify endpoint configurations in a web.config.
    <%@ ServiceHost Language="C#" Debug="true"
        Service="Barkes.Services.Presidents.PresidentsService, $SharePoint.Project.AssemblyFullName$"
        CodeBehind="PresidentsService.svc.cs"
        Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory,
        Microsoft.SharePoint.Client.ServerRuntime, Version=15.0.0.0, Culture=neutral,
        PublicKeyToken=71e9bce111e9429c" %>
  19. Open IPresidentsService.cs and enter the following interface definition, with associated ServiceContract and OperationContracts.
    1. using System;
      using System.Collections.Generic;
      using System.ServiceModel;
      using System.ServiceModel.Web;
      using Barkes.Services.Presidents.Model;
      
      namespace Barkes.Services.Presidents
      {
          [ServiceContract]
          interface IPresidentsService
          {
              [OperationContract]
              [WebGet(UriTemplate = "GetAllPresidents",
                  ResponseFormat = WebMessageFormat.Json)]
              List<President> GetAllPresidents();
      
              [OperationContract(Name = "GetPresidentsByLastName")]
              [WebGet(UriTemplate = "GetPresidentsByLastName/{lastName}",
                  ResponseFormat = WebMessageFormat.Json)]
              List<President> GetPresidentsByName(string lastName);
      
              [OperationContract(Name = "GetPresidentsByLastFirstName")]
              [WebGet(UriTemplate = "GetPresidentsByLastFirstName/{lastName}/{firstName}",
                  ResponseFormat = WebMessageFormat.Json)]
              List<President> GetPresidentsByName(string lastName, string firstName);
      
              [OperationContract]
              [WebGet(UriTemplate = "GetPresidentById/{id}",
                  ResponseFormat = WebMessageFormat.Json)]
              President GetPresidentById(string id);
      
              [OperationContract]
              [WebInvoke(Method = "POST", UriTemplate = "AddPresident",
                  RequestFormat = WebMessageFormat.Json,
                  ResponseFormat = WebMessageFormat.Json)]
              bool AddPresident(President president);
          }
      }
      
  20. Open PresidentsService.svc.cs and enter the following code to implement the service interface.
    1. using Microsoft.SharePoint.Client.Services;
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.ServiceModel.Activation;
      using Barkes.Services.Presidents.Model;
      
      namespace Barkes.Services.Presidents
      {
          [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
          public class PresidentsService : IPresidentsService
          {
              #region Private Members
      
              private List<President> _presidents;
              private List<President> Presidents
              {
                  get
                  {
                      // If there aren't any presidents in our list, populate with samples
                      _presidents = _presidents ?? new List<President>(SampleData.SamplePresidents);
                      return _presidents;
                  }
              }
      
              #endregion
      
              #region IPresidentsService Implementation
      
              public List<President> GetAllPresidents()
              {
                  return Presidents;
              }
      
              public List<President> GetPresidentsByName(string lastName)
              {
                  return GetPresidentsByName(lastName, string.Empty);
              }
      
              public List<President> GetPresidentsByName(string lastName, string firstName)
              {
                  var query = from President p in Presidents
                              where p.LastName.ToLower().Contains(lastName.ToLower())
                                 && (string.IsNullOrWhiteSpace(firstName) 
                                      ? true 
                                      : p.FirstName.ToLower().Contains(firstName.ToLower()))
                              select p;
      
                  return query.ToList();
              }
      
              public President GetPresidentById(string id)
              {
                  var query = from President p in Presidents
                              where p.Id == id
                              select p;
      
                  return query.FirstOrDefault();
              }
      
              public bool AddPresident(President president)
              {
                  Presidents.Add(president);
                  return true;
              }
      
              #endregion
      
          }
      }
  21. Add a new folder named Model to the project by right-clicking on the project and selecting Add, then New Folder.
  22. Add a new class in the Model folder named President.cs and enter the following class definition, with associated DataContract and DataMembers.
    1. using System.Runtime.Serialization;
      
      namespace Barkes.Services.Presidents.Model
      {
          [DataContract]
          public class President
          {
              [DataMember]
              public string Id { get; set; }
      
              [DataMember]
              public string LastName { get; set; }
      
              [DataMember]
              public string FirstName { get; set; }
      
              [DataMember]
              public string EmailAddress { get; set; }
          }
      }
  23. Add a new class in the Model folder named PresidentsData.cs and enter the following sample data code.  In a production application, this would typically come from a database.  The presidents array is purposely abbreviated for readability - all the presidents are in the complete source.
    1. namespace Barkes.Services.Presidents.Model
      {
          public static class SampleData
          {
              // This array is purposely abbreviated for readability in this article.
              // The complete list of presidents is available in the source download.
              public static President[] SamplePresidents = new President[]
              {
                  new President { 
                      Id =  "1", FirstName = "George", LastName = "Washington", 
                      EmailAddress = "gwashington@email.com" },
                  new President { 
                      Id =  "2", FirstName = "John", LastName = "Adams", 
                      EmailAddress = "jadams@email.com" },
                  new President { 
                      Id =  "3", FirstName = "Thomas", LastName = "Jefferson", 
                      EmailAddress = "tjefferson@email.com" },
                  new President { 
                      Id =  "4", FirstName = "James", LastName = "Madison", 
                      EmailAddress = "jmadison@email.com" },
                  new President { 
                      Id =  "5", FirstName = "James", LastName = "Monroe", 
                      EmailAddress = "jmonroe@email.com" },
                  new President { 
                      Id = "43", FirstName = "George W.", LastName = "Bush", 
                      EmailAddress = "gbush@email.com" },
                  new President { 
                      Id = "44", FirstName = "Barack", LastName = "Obama", 
                      EmailAddress = "bobama@email.com" },
              };
          }
      }
  24. Now you're ready to build the solution and deploy the WSP.  After deployment, you'll find the PresidentsService.svc service declaration in the 15 hive at C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\BarkesServices.

Call the Service from Managed Code

There are a number of different options (tools, libraries, etc) and articles available to help you consume a WCF REST service from managed code.  The following is an excerpt from a Visual Studio unit test project that calls the service to return all presidents.   The custom JSON helper class used to simplify the object (de)serialization is shown below as well.
  1. // Be sure to update the url to point to the Presidents Service in your SP farm.
    string url = "http://sp13.dev/_vti_bin/BarkesServices/PresidentsService.svc/GetAllPresidents";
    string response = CallService(url);
    List<President> presidents = JsonHelper.Deserialize<List<President>>(response);
    
    private string CallService(string serviceUrl)
    {
        WebClient client = new WebClient();
        client.UseDefaultCredentials = true;
        client.Headers["Content-type"] = "application/json";
        client.Encoding = Encoding.UTF8;
        string response = response = client.DownloadString(serviceUrl);
    
        return response;
    }
  1. public class JsonHelper
    {
        public static string Serialize<T>(T obj)
        {
            MemoryStream stream = new MemoryStream();
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
            serializer.WriteObject(stream, obj);
            stream.Position = 0;
            StreamReader reader = new StreamReader(stream);
            return reader.ReadToEnd();
        }
    
        public static T Deserialize<T>(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return default(T);
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
            MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(data));
            return (T)serializer.ReadObject(stream);
        }
    }

Call the Service from JQuery

The following demonstrates how to call the service from a Script Editor Web Part using simple HTML, JavaScript and JQuery.
  1. <script src="http://sp13.dev/SiteAssets/jquery-1.10.2.min.js"></script>
    
    <h2>SharePoint 2013: Consume a custom WCF REST service hosted in SharePoint 2013.</h2>
    <h3>This is a quick sample to demonstrate calling a custom SharePoint-hosted WCF REST service from a
        Script Editor Web Part using simple HTML, JavaScript and JQuery.
    </h3>
    
    <div>
        <br />
        <p id="message">Loading presidents...</p>
    </div>
    
    <div id="resultsPanel"></div>
    
    <script type="text/javascript">
        $(document).ready(function () {
            getPresidentsData();
        });
      
    function getPresidentsData() {
        var serviceUri = _spPageContextInfo.webAbsoluteUrl +
            "/_vti_bin/BarkesServices/PresidentsService.svc/GetAllPresidents";
        $.ajax({
            type: "GET",
            contentType: "application/json",
            url: serviceUri,
            dataType: "json",
            success:
                function (response) {
                    showPresidentsList(response);
                    $('#message').html("<a href=" + serviceUri + ">" + serviceUri + "</a>");
                },
            error:
                function (err) {
                    alert(err);
                }
        });
    }
    
    function showPresidentsList(presidentsData) {
        $.each(presidentsData, function () {
            $('#resultsPanel').append($(this)[0].Id + ' - ');
            $('#resultsPanel').append($(this)[0].FirstName + ' ');
            $('#resultsPanel').append($(this)[0].LastName + ' (');
            $('#resultsPanel').append($(this)[0].EmailAddress + ')');
            $('#resultsPanel').append('<br><br>');
        });
    }
    </script>

Results Screenshots

Calling the Presidents Service from a Script Editor Web Part using simple HTML, JavaScript and JQuery.  Of course you can use the resulting JSON data with Backbone.jsKnockout and a variety of JavaScript/JQuery grids (JS GridsimpleGridjqGrid, etc).
Interacting with the Presidents Service in Fiddler:
 

Download the source (50.8 KB)

No comments:

Post a Comment