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.

Friday, November 16, 2012

Difficulties with SP2013

I'm a little late to the party this time, but I've finally carved out some time to play with SharePoint 2013. I've noticed two things so far:
  1. SharePoint 2013 chews CPU a lot more than SharePoint 2010 did.
  2. The install has been flaky for me, perhaps due to point 1.
I've had several failed installs so far and they present weird errors when they do. The first failure I had wouldn't let me create managed accounts. I did a reinstall and failure number two wouldn't provision site collections correctly .

The site I created using the wizard resulted in this:


I created a new web application and then got this during the site collection creation:

That Correlation ID led to this error in the logs:

A large block of literal text was sent to sql (length 93794).  This can result in blocking in sql and excessive memory use on the front end.

I did three things that seem to have addressed the issue:
  1. Uninstalled SharePoint and deleted the 14- and 15-hives.
  2. Upped the RAM on my dev box from 4Gig to 8Gig.
  3. Reinstalled SharePoint

Monday, November 29, 2010

SharePoint 2010 - Replace missing Publishing Site Columns and Content Types

While working on a new content type that inherited from Article Page, I somehow lost a lot of Site Columns. When I tried to view the Article Page content type, it had no columns listed!

This ended up being a simple fix. Run the following command to reinstall the Site Columns and Content Types that are used by Publishing:

stsadm -o activatefeature -name PublishingResources -url http://MySharePointSite -force

Once that operation completes, the missing columns should now be back. Don't forget to fix the feature that caused the problem!

Wednesday, November 24, 2010

SharePoint 2010 - Content Deployment Woes Part 2: Custom Features

If you are reading this post, please read through my previous post on this topic to get some background on tricks to solving the "Could not find Feature" issue with built-in SharePoint features.

If you are still reading, then you have probably built and deployed custom features to your farm, such as a web part. You are then running into problems during Content Deployment jobs between farms that have different SharePoint versions on them, such as deploying from Enterprise to Standard.

The solution that worked for the built-in features was appropriate because the features that were causing the problems didn't need to be installed on your destination farm. But with custom features, that is not the case. You built them precisely because you need to be able to run them on the destination farm.

I encountered this same problem and it took me a little while to figure out why it was unhappy. Here are the steps I'd suggest you follow:

  • Check the 14-hive on the destination Web Front End to verify your feature is there.
  • Verify that your feature is installed in the destination site. For instance, check the Site Features page for features scoped to the Site level
  • Try enabling the feature and making sure it doesn't thrown any errors

Maybe you've done all those things and the feature looks good, but it's still not working? At this point, you should be confused. I know I was. I decided to check the settings on my package in Visual Studio. The Deployment Server Type was set to WebFrontEnd, which was correct. I then checked the 14-hive on the destination App Server, which is where Content Deployment jobs are run. No feature folder there.

Now I know what you're thinking - isn't that what we want? Solutions that are targeted as WebFrontEnd are marked that way for a reason - they don't need to be on the App Server because it's not serving up those web parts. Well, when it comes to Content Deployment, apparently they DO need to be there. I assume this is because the Content Deployment job is going through Central Administration, which is living on your App Server.

The solution I came up with was to enable the Microsoft SharePoint Foundation Web Application service on my App Server, effectively making it a Web Front End as well. This ensures that when I add solutions to my farm that the features get deployed to my App Server as well, effectively squashing the Content Deployment job errors I was receiving.

My App Server is already segmented off from the Web Front Ends by a firewall to keep it from being accessible from the internet. However, to ensure that my App Server is never used as a Web Front End, I am also making sure it is never listed in the load balancer and I am blocking traffic to port 80 on that box. This means there should be no real impact to the machine and my Content Deployment jobs can now run successfully.

SharePoint 2010 - Content Deployment Woes

Lately I've been working a lot with Publishing sites, which means I've been using Content Deployment jobs to move content between my farms. Unfortunately, I've learned the hard way that this part of SharePoint is rather particular about farm setups.

I have an Enterprise development farm that I use for consulting work. I built some pages on it and then wanted to deploy them to my customer's test farm, which runs a Standard license. This broke on me every single time. Specifically, I kept getting errors that features didn't exist on my destination farm.

One message I got was "Could not find feature IPFSSiteFeatures". Here are the screenshots from my deployment report:








These are all the features that existed on my Enterprise development farm but not on my Standard test farm:
  • IPFSSiteFeatures
  • WACustomReports
  • PPSWorkspaceCtype
  • PPSMonDatasourceCtype
  • PPSWebParts
  • PPSSiteCollectionMaster
To get my content deployment working again, I enabled them temporarily on the destination farm, performed the deployment, and then disabled them again. You might also be able to disable/uninstall those features on your Enterprise farm before doing the content deployment job and achieve success, but I didn't test that option.  
Note: Make sure you disable these features on your destination farm after deployment, or you may be in violation of your SharePoint license.

If you made the mistake of installing Excel Services and PerformancePoint Services on your Enterprise environment, your problems are more difficult. If you run Get-SPFeature, you will see more items in the list that will cause your content deployment to fail, such as BizAppsListTemplates.

You might be tempted to disable them on your source farm. Unfortunately, when I tried, I learned you cannot disable them with PowerShell because they are still in use. However, those features aren't activated on any of my sites. Because of my failure to disable them on the source farm, I did not test enabling them on the destination farm because I wasn't sure I'd be able to get rid of them and I didn't want to have to rebuild the entire farm again and lose my content.

I believe these features might be activated inside the service application itself, but I have not found a way to confirm this theory. I tried deleting the Service Application, but the features still couldn't be removed. If you figure this out, let me know.

Summary

In short, here are the things to keep in mind if you want to do a Content Deployment job between two farms:
  • Source and Destination farms should be on the same version of SharePoint
  • You must deploy solutions that exist on your source farm to your destination farm prior to running the job.
  • Features that are enabled on your source farm will automatically get enabled on your destination farm, but if you encounter an error with a feature, verify that the feature is installed on the destination
There are some additional quirks if you receive the "Could not find feature X" for custom features that you have developed. I'll cover those in a followup post.

    Wednesday, October 6, 2010

    SharePoint 2010 - Dynamic JavaScript problems with Inline Editing

    SharePoint 2010 allows inline editing of your HTML on Publishing pages, which is a nice boost to productivity. However, there are some quirks with this feature that can really trip you up if you aren't ready for them.

    Here's a scenario I ran into this week. I have a simple Publishing Page with a Content Editor Web Part on it. Inside this web part, there is an HTML element that I target with some jQuery to display tooltips. The JavaScript here is really quite simple and just dynamically adds a relative positioned div to the page above the target element.

    The code for this is tested and works fine outside of SharePoint. But when you edit a page, you'll find an interesting side-effect. Specially, when editing the page, the JavaScript that adds the dynamic elements to the DOM still runs. When the page is saved, even if you didn't even edit that particular Content Editor Web Part, the updated DOM elements get saved - including the tooltip changes!

    I have tested this with a lot of scenarios, but it's pretty consistent across any SharePoint components that provide inline editing.

    So, how do you allow JavaScript to dynamically update content on publishing pages? This is really going to depend on the nature of your content. Here are a few different scenarios I've employed in my site:
    • Extract the dynamic content and make it a web part.

      This works well if your content doesn't change that often and has few properties. A great example here would be having a web part that uses swfobject.js to load a flash file rather than placing the script call inside a Content Editor
    • Skip the JavaScript calls that update your page when in edit or design mode.


      This works really well for scenarios like my tooltip. You can do this either in JavaScript or C#, depending on how your page works.
    Here is an example of checking your page mode with JavaScript, using jQuery:
    if ($('#MSOSPWebPartManager_DisplayModeName').val() == 'Design' || $('#MSOSPWebPartManager_DisplayModeName').val() == 'Edit') {
            alert('Don't do anything');
        } else {
            alert('Safe to run JavaScript');
        }