3

Sharepoint Foundation 2010 サイトのカスタム Web パーツからロードされた JavaScript から AJAX によって呼び出すことができる WCF をセットアップしたいと考えています。Javascript 側の処理を簡素化するために、呼び出し元に Json を返す Restful サービスを提供したいと考えています。

問題は、AJAX 呼び出しでサーバーを呼び出すと、SPContext.Current が null になることです。

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"
     %>

Web サービスのコントラクトは次のとおりです。

[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;
    }
}

Web ブラウザーから 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 へのアップグレードをインストールすることで、その問題を解決したと思います。

SharePoint サイトにサインインしたユーザーのコンテキストに Web サービスがアクセスできるようにする Javascript からの AJAX 呼び出しを受け入れる javascript/a Web サービスから AJAX 呼び出しを行うにはどうすればよいですか?

4

2 に答える 2

5

私はこの問題の解決策を見つけたので、他の誰かがこの種の問題に遭遇した場合に備えて、私の発見を共有したいと思います.

基本的な問題は、Microsoft のサービス ファクトリが ajax 呼び出しに適したアクセス バインディングを追加できないという事実によって引き起こされます。これは、vti_bin フォルダーの認証マジックが発生しないことを意味します。通常のアクセスは正常に機能しますが、javascript から ajax 呼び出しでアクセスすると、実行中の svc が取得されますが、Sharepoint コンテキストは取得されません。

ファクトリを拡張してバインディングを正しいものに置き換えることで、問題を解決できます。

 <%@ 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"
         %>

新しい Factory の実装は次のとおりです。

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;
            }
        }

    }
}

結果を取得したときに結果をラップしたい場合は、WebHttpBehavior の代わりに WebScriptEnablingBehavior を使用することをお勧めします。

考慮すべき追加事項がいくつかあります。ネット上では、SP1 がないと SharePoint コンテキストも失われる可能性があるという指摘があります。問題が発生した場合は、最新のサービス パックを使用していることを確認してください。

最後に、REST サービスを構築している場合、UriTemplate を使用して目的の URL 構造を取得したくなるかもしれません。残念ながら、この記事の執筆時点では、このシナリオでは UriTemplate が Microsoft によってサポートされていないため、UriTemplate の存在に基づいて設計する前に、この問題を調査してください。

于 2012-02-20T11:00:10.887 に答える