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

时间:2011-12-14 14:51:49

标签: 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"
     %>

网络服务的合同是:

[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站点的用户的上下文?

2 个答案:

答案 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!似乎只是在提升权限之前访问此属性也使它在以后可用!怪异!!!!

所以实际上我添加的唯一代码是你上面看到的代码,后来甚至没有使用那个变量!我觉得某处有一个捕获物!