简体   繁体   English

带有webHttpRelayBinding和REST / JSON和POST的Azure中继服务总线

[英]Azure Relay Service Bus with webHttpRelayBinding with REST/JSON and POST

The following article explains how to build an simple WCF service with Azure Relay Service Bus: https://azure.microsoft.com/en-us/documentation/articles/service-bus-dotnet-how-to-use-relay/ The example shows the use with TCP Binding. 以下文章介绍了如何使用Azure中继服务总线构建简单的WCF服务: https : //azure.microsoft.com/zh-cn/documentation/articles/service-bus-dotnet-how-to-use-relay/示例显示了TCP绑定的用法。 I reproduced it and it works flawless. 我复制了它,它的工作无懈可击。 Now I want the same with webHttpRelayBinding but it does not work as expected. 现在,我想要与webHttpRelayBinding相同,但无法按预期工作。 I split the code the service in a common dll, a WCFServiceWebRole project, and command line host (as alternative to web.config) and clients : 我将服务代码拆分为一个通用的dll,一个WCFServiceWebRole项目,一个命令行主机(作为web.config的替代)和客户端:

common dll with interface definition and settings file with bus key, namespace and protocol (tcp or http) namespace WCFRelayCommon { using System.ServiceModel; 具有接口定义的通用dll和具有总线密钥,名称空间和协议(tcp或http)名称空间WCFRelayCommon {使用System.ServiceModel的设置文件 using System.ServiceModel.Web; 使用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 project. WCFServiceWebRole项目。 I first defined all service settings (behavior, biding) in the Web.config and thus should be self sufficient as is. 我首先在Web.config中定义了所有服务设置(行为,出价),因此应该是自给自足的。

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

But I also defined an alternative host project with all settings by code, easier to debug. 但是我还定义了一个替代宿主项目,其中所有设置均通过代码进行设置,易于调试。 So just a command line project using the settings file. 因此,仅是使用设置文件的命令行项目。 2 implementations: either with NetTcpRelayBinding or with WebHttpRelayBinding. 2种实现:使用NetTcpRelayBinding或WebHttpRelayBinding。 with secure transport. 运输安全。

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

And the client command line app. 和客户端命令行应用程序。

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

I just have to change the setting parameter to http or tcp to use either webHttpRelayBinding or netTcpRelayBiding 我只需要将设置参数更改为http或tcp即可使用webHttpRelayBinding或netTcpRelayBiding

As said, with netTcpRelayBiding, the code run as expected. 如前所述,使用netTcpRelayBiding,代码按预期运行。

With webHttpRelayBinding, I get a InvalidOperationException in mscorlib 使用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

What did I miss? 我错过了什么? Maybe some configuration on the Azure Portal? 也许在Azure门户上进行一些配置? I just followed the instructions from the tutorial... 我只是按照教程中的说明进行操作...

I was able to get your example working in "http" mode by changing WCFRelayClient.Program.CreateChannel method to use WebChannelFactory<T> when dealing with WebHttpBinding/WebHttpRelayBinding: 通过处理WebHttpBinding / WebHttpRelayBinding时,将WCFRelayClient.Program.CreateChannel方法更改为使用WebChannelFactory<T> ,可以使您的示例在“ http”模式下工作:

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

If you are sending with some HTTP Client other than the *HttpRelayBinding and your relay endpoint requires client authentication then you need to build the SAS Token and put the authorization into the HTTP Authorization header. 如果使用* HttpRelayBinding以外的其他HTTP客户端进行发送,并且中继端点需要客户端身份验证,则需要构建SAS令牌并将授权放入HTTP Authorization标头中。

NodeJs, JAVA, PHP, C# examples and general description of how to build the SAS Token: https://azure.microsoft.com/en-us/documentation/articles/service-bus-sas-overview/ NodeJs,JAVA,PHP,C#示例以及如何构建SAS令牌的一般说明: https ://azure.microsoft.com/zh-cn/documentation/articles/service-bus-sas-overview/

This page appears to have a Javascript example (I haven't verified it works): http://developers.de/blogs/damir_dobric/archive/2013/10/17/how-to-create-shared-access-signature-for-service-bus.aspx 该页面似乎有一个Javascript示例(我尚未验证它的工作原理): http : //developers.de/blogs/damir_dobric/archive/2013/10/17/how-to-create-shared-access-signature- for-service-bus.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM