Share via


SharePoint 2013: Create a Custom WCF REST Service Hosted in SharePoint and Deployed in a WSP

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 via 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.
    http://3.bp.blogspot.com/-rRE5_E6aRio/U18rvo5tvPI/AAAAAAAABAI/PGF3vKXPJIQ/s1600/CreateSharePointWfcService01.png

  3. Ensure the Deploy as farm solution option is selected.
    http://2.bp.blogspot.com/-Z594p2YB9wg/U18ryCqYS4I/AAAAAAAABA8/aZSBYwDCI0U/s1600/CreateSharePointWfcService02.png

  4. After the project is created, right-click on the project in the Solution Explorer, then Add -> SharePoint Mapped Folder.
    http://1.bp.blogspot.com/-aJPuwXw5N4Q/U18rvus05SI/AAAAAAAABAE/MwJBStj7XpY/s1600/CreateSharePointWfcService03.png

  5. On the Add SharePoint Mapped Folder dialog, select the ISAPI folder and click OK.
    http://3.bp.blogspot.com/-Io3WIPylde8/U18rwN5nVfI/AAAAAAAABAQ/EOK-rLuFKf8/s1600/CreateSharePointWfcService04.png

  6. Right-click on the ISAPI folder in the Solution Explorer, then Add -> New Item.
    http://1.bp.blogspot.com/-R_enyCaGB_M/U18rwco9LjI/AAAAAAAABAY/xnvV-ooAj4M/s1600/CreateSharePointWfcService05.png

  7. On the Add New Item dialog, select Text File from the General category and enter ** PresidentsService.svc*** as the name, then click* Add.  Make sure to change the default file extension from txt to svc.
    http://1.bp.blogspot.com/-cIsHZvp_5jc/U18rwol18LI/AAAAAAAABAg/Ypm1lx3ptoo/s1600/CreateSharePointWfcService06.png 

  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 click Add.
    http://1.bp.blogspot.com/-rmWZGQXldj0/U18rxAGOU0I/AAAAAAAABAo/f1rYltgb6wg/s1600/CreateSharePointWfcService07.png

  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.
    http://3.bp.blogspot.com/-ni6yaxuWXbc/U18rxbmkKqI/AAAAAAAABA0/KHMogxE1Xgg/s1600/CreateSharePointWfcService08.png

  12. Add the required assembly references by right-clicking References, then Add Reference from the Solution Explorer.
    http://3.bp.blogspot.com/-e1B2jSUUIyw/U18rx5CqaqI/AAAAAAAABA4/cyBhmGasXoU/s1600/CreateSharePointWfcService09.png

  13. On the Reference Manager dialog, select Framework and check System.Runtime.Serialization, System.ServiceModel and System.ServiceModel.Web, then click OK.
    http://2.bp.blogspot.com/-w8OpGjUELio/U18ryFfbIZI/AAAAAAAABBU/Vcj_DDQgKiU/s1600/CreateSharePointWfcService10.png

  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.
    http://1.bp.blogspot.com/-fm0mwCmJl68/U18rybmhzkI/AAAAAAAABBQ/UXvzHhNTDME/s1600/CreateSharePointWfcService11.png

  15. Right-click the project in the Solution Explorer, then Edit Barkes.Services.Presidents.csproj.
    http://2.bp.blogspot.com/-7fhhIdoVEbc/U18ryj_ERVI/AAAAAAAABBM/q-3iJzeCA3k/s1600/CreateSharePointWfcService12.png

  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.
    http://2.bp.blogspot.com/-1ZqYNB2tfXQ/U18rzJYuO_I/AAAAAAAABBk/SOq1UyveAaY/s1600/CreateSharePointWfcService13.png

  18. Open PresidentsService.svc and enter the following service declaration.  Note that the use of the SharePoint-specific MultipleBaseAddressWebServiceHostFactory 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 Knockout and a variety of JavaScript/JQuery grids (JS Grid, simpleGrid, jqGrid, etc).

http://1.bp.blogspot.com/-wKzWg32-3tc/U184xRVYr3I/AAAAAAAABB0/7RPbDDRLnW8/s1600/CreateSharePointWfcService14.png

Interacting with the Presidents Service in Fiddler:

http://4.bp.blogspot.com/-wuUpFfkK9Vw/U184xTMXkWI/AAAAAAAABB4/DiHtDv6snZg/s1600/CreateSharePointWfcService15.png

Download the source (50.8 KB)

For more SharePoint articles and samples, check out the Software Development Outpost.