Tuesday, August 13, 2013

Creating a custom WCF REST service for SharePoint 2013 without a Visual Studio template

Overview

Recently a colleague asked me how I built REST services for SharePoint 2013, since there is no template for this in Visual Studio 2012. While I have worked with some of the community built templates in the past, they generally require installing additional things on your development server, which really isn't necessary. You can actually stand up a REST service in SharePoint 2013 rather quickly, with just Visual Studio 2012.

Let's start with a video walking you through the process. The video follows the process outlined further down the post. If you want to view the full resolution video, you can download it from Dropbox. You can also download the code built in the video from Dropbox.



Creating the REST service

The basic process for creating your REST service are the following steps, which were demonstrate in the video.

  1. Create a new SharePoint 2013 Empty Project as a Farm Solution
  2. Add a WCF Service Application project to your solution
  3. Add the ISAPI mapped folder to your SharePoint project
  4. Drag the Service1.svc and Service1.svc.cs files from the WCF project to the SharePoint project under the ISAPI mapped folder
  5. Drag the IService1.cs file from the WCF project to the SharePoint project
  6. Add the WCF references to your SharePoint project
    • System.Runtime.Serialization
    • System.ServiceModel
    • System.ServiceModel.Web
  7. Change the namespace from WcfService1 to your assembly's namespace (e.g., MyRestService) in Service1.svc.cs and IService1.cs
  8. Build your project
  9. Using the Visual Studio Command Prompt, go to the project output folder of the SharePoint project
  10. Get the PublicKeyToken for your assembly using the strong name utility
    • sn -T MyRestService.dll
  11. In Service1.svc, change the Service attribute on the ServiceHost directive to be in the following format:
  12. <%@ ServiceHost Language="C#" Debug="true" Service="{NameSpace}.{ServiceName}, {AssemblyName}, Version=1.0.0.0, Culture=Neutral, PublicKeyToken={PublicKeyToken}" CodeBehind="Service1.svc.cs" %>
    
    • Replace the tokens here, represented by {Token} with the appropriate values for your project. e.g., 
    <%@ ServiceHost Language="C#" Debug="true" Service="MyRestService.Service1, MyRestService, Version=1.0.0.0, Culture=Neutral, PublicKeyToken={PublicKeyToken}" CodeBehind="Service1.svc.cs" %>
  13. Deploy your solution
  14. Verify the service loads
    • Note: see below for troubleshooting a common error.
  15. Add a HelloWorld operation to your contract, IService1.cs that is set up for REST.
  16. [OperationContract]
    [WebGet(ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate = "HelloWorld")
    string HelloWorld();
    
  17. Implement the operation in Service1.svc.cs
  18. public string HelloWorld()
    {
        return "Hello World";
    }
    
  19. Update web.config with the appropriate info for your service
<behaviors>
    <serviceBehaviors>
        <behavior name="Service1ServiceBehavior">
            <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
            <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
    </serviceBehaviors>
    <endpointBehaviors>
        <behavior name="jsonBehavior">
            <webHttp />
        </behavior>
    </endpointBehaviors>
</behaviors>
<services>
    <service name="MyRestService.Service1" behaviorConfiguration="Service1ServiceBehavior">
        <endpoint address="" binding="webHttpBinding" behaviorConfiguration="jsonBehavior" contract="MyRestService.IService1" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    </service>
</services>
  1. Test your HelloWorld operation and you should see the following:


Troubleshooting

  • If you get an error that says "This collection already contains an address with scheme http.  There can be at most one address per scheme in this collection." this is due to having multiple bindings in IIS, which is common in SharePoint when you have multiple Alternate Access Mappings.
This collection already contains an address with scheme http.  There can be at most one address per scheme in this collection.
    • In web.config, add the multiplesitebindingsenabled attribute to the servicehostingenvironment element.
    • <servicehostingenvironment aspnetcompatibilityenabled="true" multiplesitebindingsenabled="true"%></servicehostingenvironment%>
      

Monday, May 13, 2013

SharePoint 2013 Searches return no results even though content sources were crawled successfully

Recently, a client of mine using SharePoint 2013 changed the URL of their site. A couple of weeks later, they noticed that search was not returning any results. I checked in ULS and noticed some errors indicating that the Content Source had not been updated with the correct URL. I made the change in search administration and did a recrawl. The crawl found several thousand items and had no errors.

Unfortunately, we still weren't getting any search results. The problem didn't appear to be with the index, but I cleared the index and rebuilt it just to be sure. Still no results. I also noticed that there was nothing useful in ULS to help me track down the problem.

On a suggestion from a colleague, I created a new web application and associated it with the same search service application. I added some dummy content to that new web application and indexed it. When I ran a search from within that search application, I was getting results. Progress - now I knew that my search service application appeared healthy.

We decided to see if the problem was the content or the site itself. We took a backup of the content from the production web application and restored it to the new web application we had just created. We then rebuilt the index and ran a test. Search worked perfectly. So our problem wasn't the content.

Now that we had narrowed down the problem to our web application, we had a pretty easy fix. We waited for a maintenance window and then removed the web application. We then provisioned a new web application with the correct settings for our production environment and attached the content database from the original. After another rebuild of our index, we had working search results.