wcf - 使用 AJAX 访问 Sharepoint WCF 时 SPContext.Current null

标签 wcf sharepoint-2010 claims

我想设置一个 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/

相关文章:

WCF 2-way ssl 不工作

javascript - 为什么我的数据绑定(bind)不起作用?

security - MVC5 AntiForgeryToken 声明/"Sequence contains more than one element"

volume - 如何防止两个卷声明在 Kubernetes 上声明相同的卷?

algorithm - 最短路径树声明(图)

c# - 通过单独的应用程序控制服务

wcf - 如何在WCF中使用MSMQ?

wcf - 我需要将哪些 WCF 客户端文件 checkin SVN

sharepoint - 使用 SharePoint 客户端对象模型检查列表列是否存在?

在 Windows XP 32 位上进行 Sharepoint 2010 开发?