我想设置一个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"
%>
网络服务的合同是:
[ServiceContract(Namespace = "", ProtectionLevel= ProtectionLevel.None)]
public interface ISuggestServiceTest
{
[WebGet(UriTemplate = "/SuggestAddress/{streetprefix}/", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
Dictionary<string, GenericAddress> SuggestAddress(string streetprefix);
}
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;
}
}
我已经验证过,如果我从webbrowser调用web服务,一切都按预期工作,并且我得到了正确的数据。
我使用以下Ajax调用
$.ajax({
url: addressUrl + "/"+request.term,
dataType: 'json',
success: function (data) {
responseCallback(data);
$(this).removeClass("fetching");
}
});
使用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调用,以允许Web服务访问已登录到Sharepoint站点的用户的上下文?
答案 0 :(得分:5)
我确实找到了这个问题的解决方案,所以我想分享我的发现,以防其他人偶然发现这种问题。
基本问题是由于微软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"
%>
新工厂的实施如下
public class AjaxCompatibleRestServiceHostFactory : Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new AjaxCompatibleRestServiceHost(serviceType, baseAddresses);
}
}
最后是用于替换绑定的实际代码
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;
}
}
}
}
如果您希望在返回结果时将结果包装,则可能需要使用WebScriptEnablingBehavior而不是WebHttpBehavior。
还有一些需要考虑的事项。网上有一些迹象表明缺少SP1也可能导致缺少Sharepoint上下文,因此如果遇到问题,请验证您是否拥有最新的Service Pack。
最后,如果您正在构建REST服务,可能很有可能使用UriTemplate来获取所需的URL结构。不幸的是,在撰写本文时,微软在这种情况下不支持UriTemplate,因此在您的设计基于UriTemplate的存在之前,请先研究这个问题。
答案 1 :(得分:4)
我在使用SharePoint 2013时遇到了同样的问题。我没有机会尝试您的解决方案,因为我读到了另一个基本上获取当前站点和Web ID,然后提升权限然后重新创建它们。像这样的东西: https://sharepoint.stackexchange.com/questions/74205/spcontext-current-is-null-for-ihttphandler
但是,出于意外,我发现了以下内容。获取当前网站的ID后,如下所示: var currentSiteId = SPContext.Current.Site.ID;
稍后在提升的权限委托内,SPContext.Current不再为null!似乎只是在提升权限之前访问此属性也使它在以后可用!怪异!!!!
所以实际上我添加的唯一代码是你上面看到的代码,后来甚至没有使用那个变量!我觉得某处有一个捕获物!