我想设置一个 WCF,可以通过 AJAX 从我们的 Sharepoint Foundation 2010 站点上的自定义 Web 部件加载的 javascript 调用。为了简化 Javascript 端的处理,我想提供一个 Restful 服务,将 Json 返回给调用者。
问题是当我使用 AJAX 调用服务器时,SPContext.Current 为空。
我正在使用 svc 文件中的 MultipleBaseAddressWebServiceHostFactory 来创建 web 服务
<%@ 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"
%>
网络服务的契约(Contract)是:
[ServiceContract(Namespace = "", ProtectionLevel= ProtectionLevel.None)]
public interface ISuggestServiceTest
{
[WebGet(UriTemplate = "/SuggestAddress/{streetprefix}/", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
Dictionary<string, GenericAddress> SuggestAddress(string streetprefix);
}
webservice的实现基本如下。
[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;
}
}
我已经验证,如果我从网络浏览器调用网络服务,一切都会按预期工作,并且我会返回正确的数据。
我使用下面的 Ajax 调用
$.ajax({
url: addressUrl + "/"+request.term,
dataType: 'json',
success: function (data) {
responseCallback(data);
$(this).removeClass("fetching");
}
});
使用 Firebug,我已验证从 javascript 调用了正确的 URL,并且我已在服务器端验证确实到达了正确的代码,但 SPContext.Current 为空。
Sharepoint 服务器使用 Windows 和声明进行登录。这意味着实际的 WCF 将使用与 Sharepoint 解决方案不同的帐户运行,但由于我部署到 vti_bin 下面的文件夹,Sharepoint 应该将其上下文提供给 WCF。在我看来,AJAX 调用不会触发 Sharepoint 提供其上下文,在某种意义上它是匿名的。
起初我认为网络服务本身是罪魁祸首,因为它在从浏览器调用时会随机失败,但我认为我通过安装 Sharepoint Foundation 2010 升级解决了这个问题。
如何从 javascript/接受来自 Javascript 的 AJAX 调用的网络服务进行 AJAX 调用,从而允许网络服务访问已登录到 Sharepoint 站点的用户的上下文?
最佳答案
我确实找到了这个问题的解决方案,所以我想分享我的发现,以防其他人偶然遇到这种问题。
基本问题是由于微软的 svc 工厂未能为 ajax 调用添加合适的访问绑定(bind)。这意味着 vti_bin 文件夹的身份验证魔法不会发生。你得到一个正在运行的 svc,但是当你从你的 javascript 使用 ajax 调用访问它时没有 Sharepoint 上下文,即使普通访问工作正常。
您可以通过扩展工厂以将绑定(bind)替换为正确的绑定(bind)来解决此问题
<%@ 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);
}
}
最后是替换绑定(bind)的实际代码
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 上下文,因此如果遇到问题,请验证您是否拥有最新的服务包。
最后,如果您正在构建 REST 服务,可能会很想使用 UriTemplate 来获取您想要的 URL 结构。不幸的是,在撰写本文时,Microsoft 在这种情况下不支持 UriTemplate,因此在将您的设计基于 UriTemplate 的存在之前调查此问题。
关于wcf - 使用 AJAX 访问 Sharepoint WCF 时 SPContext.Current null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8506476/