Per*_*aav 3 wcf sharepoint-2010 claims
我想设置一个WCF,可以通过AJAX从我们的Sharepoint Foundation 2010网站上的自定义webpart加载的javascript调用.为了简化Javascript方面的处理,我想提供一个Restful服务,让Json回到调用者.
问题是,当我使用AJAX调用调用服务器时,SPContext.Current为null.
我在svc文件中使用MultipleBaseAddressWebServiceHostFactory来创建webservice
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$"%>
<%@ServiceHost Language="C#" Debug="true"
Service="Driftportalen.LvService.SuggestService"
Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHostFactory, Microsoft.SharePoint.Client.ServerRuntime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>
Run Code Online (Sandbox Code Playgroud)
Web服务的合同是:
[ServiceContract(Namespace = "", ProtectionLevel= ProtectionLevel.None)]
public interface ISuggestServiceTest
{
[WebGet(UriTemplate = "/SuggestAddress/{streetprefix}/", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
Dictionary<string, GenericAddress> SuggestAddress(string streetprefix);
}
Run Code Online (Sandbox Code Playgroud)
Web服务的实现基本如下.
[Guid("BA6733B3-F98D-4AD8-837D-7673F8BC527F")]
[BasicHttpBindingServiceMetadataExchangeEndpoint]
[ServiceBehavior(IncludeExceptionDetailInFaults = true, AddressFilterMode = AddressFilterMode.Any)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class SuggestService : ISuggestServiceTest
{
private SPWeb currentWeb;
public SPWeb CurrentWeb
{
get
{
if (currentWeb == null)
{
var siteUrl = SPContext.Current.Web.Url;
SPSecurity.RunWithElevatedPrivileges(delegate
{
using (var site = new SPSite(siteUrl))
using (var web = site.OpenWeb())
{
currentWeb = web;
}
});
}
return currentWeb;
}
}
public Dictionary<string, GenericAddress> SuggestAddress(string streetprefix)
{
LvService lvService = new LvService(CurrentWeb);
Dictionary<string, GenericAddress> suggestions = new Dictionary<string, GenericAddress>();
//SNIP
//Code that uses lvService to populate suggestions
return suggestions;
}
}
Run Code Online (Sandbox Code Playgroud)
我已经验证过,如果我从webbrowser调用web服务,一切都按预期工作,我得到了正确的数据.
我使用以下Ajax调用
$.ajax({
url: addressUrl + "/"+request.term,
dataType: 'json',
success: function (data) {
responseCallback(data);
$(this).removeClass("fetching");
}
});
Run Code Online (Sandbox Code Playgroud)
使用Firebug我已经验证了从javascript调用了正确的URL,并且我已经在服务器端验证确实已到达正确的代码,但SPContext.Current为null.
Sharepoint服务器使用Windows和Claims进行登录.这意味着实际的WCF将使用与Sharepoint解决方案不同的帐户运行,但由于我部署到vti_bin以下的文件夹,因此Sharepoint应向WCF提供其上下文.在我看来,AJAX调用不会触发Sharepoint提供其上下文,在某种意义上它是匿名的.
起初我认为Web服务本身就应该受到责备,因为当从浏览器调用时它会随机失败,但我想我通过安装升级到Sharepoint Foundation 2010来解决这个问题.
如何从接受来自Javascript的AJAX调用的javascript/web服务进行AJAX调用,以允许webservice访问已登录到Sharepoint站点的用户的上下文?
我确实找到了这个问题的解决方案,所以我想分享我的发现,以防其他人偶然发现这种问题.
基本问题是由于微软svc工厂未能为ajax调用添加适合的访问绑定这一事实引起的.这意味着vti_bin文件夹的身份验证魔法不会发生.你得到一个正在运行的svc,但是当你使用javascript中的ajax调用访问它时没有Sharepoint上下文,即使普通访问工作正常.
您可以通过扩展Factory来使用正确的绑定替换绑定来解决问题
<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$"%>
<%@ServiceHost Language="C#" Debug="true"
Service="Driftportalen.LvService.SuggestService, $SharePoint.Project.AssemblyFullName$"
Factory="Driftportalen.LvService.AjaxCompatibleRestServiceHostFactory,Driftportalen.LvService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ab8de4d18e388c1f"
%>
Run Code Online (Sandbox Code Playgroud)
新工厂的实施如下
public class AjaxCompatibleRestServiceHostFactory : Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new AjaxCompatibleRestServiceHost(serviceType, baseAddresses);
}
}
Run Code Online (Sandbox Code Playgroud)
最后是实际代码来替换绑定
public class AjaxCompatibleRestServiceHost : Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHost
{
public AjaxCompatibleRestServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
protected override void OnOpening()
{
base.OnOpening();
foreach (ServiceEndpoint endpoint in base.Description.Endpoints)
{
if (((endpoint.Binding != null) && (endpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>() != null)) && (endpoint.Behaviors.Find<WebScriptEnablingBehavior>() == null))
{
// try remove any previous behaviours
while (endpoint.Behaviors.Count > 0)
{
endpoint.Behaviors.RemoveAt(0);
}
endpoint.Behaviors.Add(new WebHttpBehavior());
}
}
ServiceDebugBehavior debug = this.Description.Behaviors.Find<ServiceDebugBehavior>();
// if not found - add behavior with setting turned on
if (debug == null)
{
this.Description.Behaviors.Add(
new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });
}
else
{
// make sure setting is turned ON
if (!debug.IncludeExceptionDetailInFaults)
{
debug.IncludeExceptionDetailInFaults = true;
}
}
ServiceMetadataBehavior metadata =this.Description.Behaviors.Find<ServiceMetadataBehavior>();
// if not found - add behavior with setting turned on
if (metadata == null)
{
this.Description.Behaviors.Add(
new ServiceMetadataBehavior() { HttpGetEnabled = true });
}
else
{
// make sure setting is turned ON
if (!metadata.HttpGetEnabled)
{
metadata.HttpGetEnabled = true;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
可能您可能希望使用WebScriptEnablingBehavior而不是WebHttpBehavior,如果您希望在返回结果时将结果包装起来.
还有一些需要考虑的事情.网上有一些迹象表明缺少SP1也可能导致缺少Sharepoint上下文,因此如果遇到问题,请验证您是否拥有最新的Service Pack.
最后,如果您正在构建REST服务,可能很有可能使用UriTemplate来获取所需的URL结构.不幸的是,在撰写本文时,微软在这种情况下不支持UriTemplate,因此在您的设计基于UriTemplate的存在之前,请先研究这个问题.