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

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

Per*_*aav 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"
         %>
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的存在之前,请先研究这个问题.