Call a custom RESTful WCF Web Service from SharePoint hosted App via CORS
Context: I was working on a SharePoint hosted app. During the development, I need to get the list of all web applications from SharePoint. Out of the box, JSOM and Restful web services doesn’t provide an interface to get a list of web application so I decided to write my own custom WCF Restful web service. Once, I am done writing the Webservice, I tried to access it via Javascript(Ajax call), but I recieved an error: 401 (Unauthorized). So, what was the issue? I was trying to call a cross domain restful webservice from my App domain and it also requires windows authentication. After doing some research, I found that I have manipulate Http headers to execute my cross domian calls and I did it via CORS (Cross Origin Resource Sharing)
Solution: In this context, the following is the code for my Restful WebService;
STEP 1: Create a RestFul WCF Service
// IFarmSolutions.cs
using Microsoft.Ajax.Samples;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace Company.FarmSolutionsWS
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
interface IFarmSolutions
{
[WebGet(UriTemplate = "GetAllFarmSolutions",
ResponseFormat = WebMessageFormat.Json)]
//[JSONPBehavior(callback = "method")]
[OperationContract]
List GetAllFarmSolutions();
[WebGet(UriTemplate = "GetAllWebApplications",
ResponseFormat = WebMessageFormat.Json)]
//[JSONPBehavior(callback = "method")]
[OperationContract]
List GetAllWebApplications();
// TODO: Add your service operations here
}
}
///////////////FarmSolutions.svc.cs/////////////
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Client.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;
namespace Company.FarmSolutionsWS
{
//[BasicHttpBindingServiceMetadataExchangeEndpointAttribute]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class FarmSolutions : IFarmSolutions
{
public List GetAllFarmSolutions()
{
List strFarmSolution = new List();
foreach (SPSolution solution in SPFarm.Local.Solutions)
{
strFarmSolution.Add(solution.Name);
}
return strFarmSolution;
}
public List GetAllWebApplications()
{
List list_webapplications = new List();
SPServiceCollection services = SPFarm.Local.Services;
foreach (SPService service in services)
{
if (service is SPWebService)
{
SPWebService webService = (SPWebService)service;
foreach (SPWebApplication webApplication in webService.WebApplications)
{
if (webApplication.DisplayName != "SharePoint Central Administration v4")
{
WebApplication oWebApplication = new WebApplication();
oWebApplication.DisplayName = webApplication.DisplayName;
oWebApplication.Name = webApplication.Name;
oWebApplication.Url = webApplication.GetResponseUri(SPUrlZone.Default).ToString();
list_webapplications.Add(oWebApplication);
}
}
}
}
return list_webapplications;
}
}
}
STEP 2: Create a Cross Domain Handler
Once I had my SharePoint
Restful Web service, I need to access it via my SharePoint hosted App. Since
SharePoint App is running in its own domain, I wrote a cross domain handler (HTTP Module) by
following the link . The purpose of writing the handler(HTTP Module) is to solve the authentication problem by adding the same origin policy to the HTTP header response in the preflight phase at the server. In this regard, I created a console application and added the following lines to code into it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
namespace ns_SPCrossDomainHandler
{
public class SPCrossDomainHandler : IHttpModule
{
public void Dispose()
{
throw new NotImplementedException();
}
public void Init(HttpApplication context)
{
context.PreSendRequestHeaders += (sender, e) =>
{
var response = context.Response;
if (!string.IsNullOrEmpty(context.Request.Headers["Origin"]) &&
context.Request.Headers["Origin"].IndexOf("app") != -1)
{
response.AddHeader("Access-Control-Allow-Origin", context.Request.Headers["Origin"]);
response.AddHeader("Access-Control-Allow-Methods", "*");
response.AddHeader("Access-Control-Allow-Credentials", "true");
if (context.Request.HttpMethod.ToUpperInvariant() == "OPTIONS")
{
response.StatusCode = (int)HttpStatusCode.OK;
}
}
};
}
}
}
Compile the SPCrossDomainHandler project and place the dlls in the BIN folder of your webapplication. In my case the path was: C:\inetpub\wwwroot\wss\VirtualDirectories\test.spsite.group80\bin. Now open the web.config and add the following entry under modules tag
STEP 3: Call your custom RestFul WCF service via Javascript
Now you can ready to execute cross domain calls since our cross domain handler is deployed and it will take care of your calls by adding necessary headers in the request. The following function I used to execute cross domain calls from my SharePoint hosted app to my custom Restful service;
function getWebMethodViaCORS(serviceUri) {
var serviceUri = "https://test.mysite/sites/spApps/_vti_bin/FarmSolutionsWS/FarmSolutions.svc/GetAllWebApplications";
var deferred = $.Deferred();
$.ajax({
crossDomain: true,
xhrFields: {
'withCredentials': true
},
url: serviceUri,
success: function (data, status) {
deferred.resolve(data);
},
failure: function (xhr, status, error) {
console.log(xhr);
deferred.reject(xhr);
}
});
return deferred.promise();
}
cheers.. :)
Reference: http://www.silver-it.com/node/159
No comments:
Post a Comment