-1

次の記事では、Azure Relay Service Bus を使用して単純な WCF サービスを構築する方法について説明してい ます 。例は、TCP バインディングでの使用を示しています。私はそれを再現し、完璧に動作します。今、私は webHttpRelayBinding でも同じことを望んでいますが、期待どおりに機能しません。サービスのコードを、共通の dll、WCFServiceWebRole プロジェクト、およびコマンド ライン ホスト (web.config の代わりとして) とクライアントに分割します。

バス キー、名前空間、およびプロトコル (tcp または http) を含むインターフェイス定義と設定ファイルを含む共通 dll 名前空間 WCFRelayCommon { using System.ServiceModel; System.ServiceModel.Web の使用;

    [ServiceContract(Namespace = "urn:ps")]
    public interface IProblemSolver
    {
        [OperationContract
        WebInvoke(UriTemplate = "/solver", Method = "POST",
            RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,
            BodyStyle = WebMessageBodyStyle.Wrapped)]
        int AddNumbers(int a, int b);
    }

    public interface IProblemSolverChannel : IProblemSolver, IClientChannel { }

    public enum BusProtocol { tcp, http };

    public class Utils
    {   
        public static BusProtocol Protocol
        {
            get
            {
                BusProtocol mode;
                if (!Enum.TryParse(AzureSettings.Default.Protocol, out mode))
                {
                    throw new ArgumentException("wrong input, exiting");
                }
                return mode;
            }
        }
    }
}

WCFServiceWebRole プロジェクト。最初に Web.config ですべてのサービス設定 (動作、入札) を定義したので、そのままで十分です。

namespace WCFServiceWebRoleRelay
{
    public class ProblemSolver : WCFRelayCommon.IProblemSolver
    {
        public int AddNumbers(int a, int b)
        {
            return a + b;
        }
    }
}

しかし、デバッグしやすいように、すべての設定をコードで行う代替ホスト プロジェクトも定義しました。したがって、設定ファイルを使用したコマンド ライン プロジェクトのみです。2 つの実装: NetTcpRelayBinding または WebHttpRelayBinding を使用。安全な輸送で。

namespace WCFRelayHost
{
    class Program
    {
        static void Main(string[] args)
        {
            var transportClientEndpointBehavior = new TransportClientEndpointBehavior
            {
                TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("RootManageSharedAccessKey", AzureSettings.Default.BusKey)
            };

            ServiceHost sh;
            switch (Utils.Protocol)
            {
                case BusProtocol.http:
                    sh = CreateWebHost(AzureSettings.Default.BusNamespace, transportClientEndpointBehavior);
                    break;
                case BusProtocol.tcp:
                    sh = CreateTcpBindingHost(AzureSettings.Default.BusNamespace, transportClientEndpointBehavior);
                    break;
                default:
                    throw new Exception("wrong mode");
            }
            sh.Open();
            Console.WriteLine("Press ENTER to close");
            Console.ReadLine();
            sh.Close();
        }

        static ServiceHost CreateTcpBindingHost(string busNamespace, TransportClientEndpointBehavior transportClientEndpointBehavior)
        {
            Uri tcpAddress = ServiceBusEnvironment.CreateServiceUri("sb", busNamespace, "solver");
            ServiceHost sh = new ServiceHost(typeof(ProblemSolver));            
            var binding = new NetTcpRelayBinding(EndToEndSecurityMode.Transport, new RelayClientAuthenticationType());            
            return AddServiceEndpoint(sh, binding, tcpAddress, transportClientEndpointBehavior);
        }

        static ServiceHost CreateWebHost(string busNamespace, TransportClientEndpointBehavior transportClientEndpointBehavior)
        {
            // https://<namespace>.servicebus.windows.net/solver
            Uri webAddress = ServiceBusEnvironment.CreateServiceUri("https", busNamespace, "solver");
            var binding = new WebHttpRelayBinding(EndToEndWebHttpSecurityMode.Transport, new RelayClientAuthenticationType());
            WebServiceHost wsh = new WebServiceHost(typeof(ProblemSolver), webAddress);
            return AddServiceEndpoint(wsh, binding, webAddress, transportClientEndpointBehavior);
        }

        static ServiceHost AddServiceEndpoint(ServiceHost sh, Binding binding, Uri uri, TransportClientEndpointBehavior transportClientEndpointBehavior)
        {
            sh.AddServiceEndpoint(typeof(IProblemSolver), binding, uri).Behaviors.Add(transportClientEndpointBehavior);            
            return sh;
        }
    }
}

そして、クライアント コマンド ライン アプリ。

namespace WCFRelayClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Enter 2 numbers to add separated by space");
            var line = Console.ReadLine();
            var array = line.Split(' ');
            int first, second;
            if (!int.TryParse(array[0], out first) || !int.TryParse(array[1], out second))
            {
                Console.WriteLine("wrong input, exiting");
            }
            else
            {
                Console.WriteLine("Wait the host to run, press ENTER when ready to send the request");
                Console.ReadLine();

                var transportClientEndpointBehavior = new TransportClientEndpointBehavior
                {
                    TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("RootManageSharedAccessKey", AzureSettings.Default.BusKey)
                };

                IProblemSolverChannel ch;
                switch (Utils.Protocol)
                {
                    case BusProtocol.http:
                        ch = WebBinding(AzureSettings.Default.BusNamespace, transportClientEndpointBehavior);
                        break;
                    case BusProtocol.tcp:
                        ch = TcpBinding(AzureSettings.Default.BusNamespace, transportClientEndpointBehavior);
                        break;
                    default:
                        throw new Exception("wrong mode");
                }
                Console.WriteLine(ch.AddNumbers(first, second));
                ch.Dispose();
            }

            Console.ReadLine();
        }

        static IProblemSolverChannel TcpBinding(string busNamespace, TransportClientEndpointBehavior transportClientEndpointBehavior)
        {
            var binding = new NetTcpRelayBinding(EndToEndSecurityMode.Transport, new RelayClientAuthenticationType());            
            var uri = ServiceBusEnvironment.CreateServiceUri("sb", busNamespace, "solver");
            return CreateChannel(binding, uri, transportClientEndpointBehavior);
        }

        static IProblemSolverChannel WebBinding(string busNamespace, TransportClientEndpointBehavior transportClientEndpointBehavior)
        {
            var binding = new WebHttpRelayBinding(EndToEndWebHttpSecurityMode.Transport, new RelayClientAuthenticationType());
            var uri = ServiceBusEnvironment.CreateServiceUri("https", busNamespace, "solver");            
            return CreateChannel(binding, uri, transportClientEndpointBehavior);
        }

        static IProblemSolverChannel CreateChannel(Binding binding, Uri uri, TransportClientEndpointBehavior transportClientEndpointBehavior)
        {
            var cf = new ChannelFactory<IProblemSolverChannel>(binding, new EndpointAddress(uri));
            cf.Endpoint.Behaviors.Add(transportClientEndpointBehavior);
            return cf.CreateChannel();
        }
    }
}

webHttpRelayBinding または netTcpRelayBiding のいずれかを使用するには、設定パラメーターを http または tcp に変更するだけです。

前述のとおり、netTcpRelayBiding を使用すると、コードは期待どおりに実行されます。

webHttpRelayBinding を使用すると、mscorlib で InvalidOperationException が発生します

System.InvalidOperationException was unhandled
HResult=-2146233079
  Message=Manual addressing is enabled on this factory, so all messages sent must be pre-addressed.
  Source=mscorlib

私は何を取りこぼしたか?おそらく、Azure ポータルでの構成でしょうか。チュートリアルの指示に従っただけです...

4

1 に答える 1

3

WebHttpBinding/WebHttpRelayBinding を処理するときWCFRelayClient.Program.CreateChannelに使用するメソッドを変更することで、例を「http」モードで動作させることができました。WebChannelFactory<T>

static IProblemSolverChannel CreateChannel(Binding binding, Uri uri, TransportClientEndpointBehavior transportClientEndpointBehavior)
{
    ChannelFactory<IProblemSolverChannel> cf;
    if (binding is WebHttpBinding || binding is WebHttpRelayBinding)
    {
        cf = new WebChannelFactory<IProblemSolverChannel>(binding, uri);
    }
    else
    {
        cf = new ChannelFactory<IProblemSolverChannel>(binding, new EndpointAddress(uri));
    }

    cf.Endpoint.Behaviors.Add(transportClientEndpointBehavior);
    return cf.CreateChannel();
}

*HttpRelayBinding 以外の HTTP クライアントを使用して送信し、リレー エンドポイントでクライアント認証が必要な場合は、SAS トークンを作成し、承認を HTTP Authorization ヘッダーに入れる必要があります。

NodeJs、JAVA、PHP、C# の例、および SAS トークンの作成方法の一般的な説明: https://azure.microsoft.com/en-us/documentation/articles/service-bus-sas-overview/

このページには Javascript の例があるようです (動作確認はしていません): http://developers.de/blogs/damir_dobric/archive/2013/10/17/how-to-create-shared-access-signature- for-service-bus.aspx

于 2016-08-15T19:13:13.323 に答える