Azure中继服务总线与webHttpRelayBinding与REST/JSON和POST

本文关键字:JSON POST REST webHttpRelayBinding 服务 总线 Azure | 更新日期: 2023-09-27 18:02:29

下面的文章解释了如何使用Azure中继服务总线构建一个简单的WCF服务:https://azure.microsoft.com/en-us/documentation/articles/service-bus-dotnet-how-to-use-relay/这个例子展示了TCP绑定的用法。我复制了它,效果完美无瑕。现在我想与webHttpRelayBinding相同,但它不像预期的那样工作。我将服务代码拆分为一个公共dll、一个WCFServiceWebRole项目、命令行主机(作为web.config的替代方案)和客户端:

带有接口定义的通用DLL和带有总线密钥、命名空间和协议(tcp或http)的设置文件名称空间WCFRelayCommon{使用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上的所有服务设置(行为、请求)。,因此应该是自给自足的。

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

但是我还定义了一个替代的主机项目,所有设置都是通过代码进行的,更容易调试。所以这只是一个使用设置文件的命令行项目。2实现:无论是netttcprelaybinding或与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();
        }
    }
}

我只需要改变设置参数为http或tcp使用webHttpRelayBinding或netttcprelaybinding

如前所述,使用netttcprelaybids,代码按预期运行。

与webHttpRelayBinding,我得到一个InvalidOperationException在mscorlib

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门户上的一些配置?

Azure中继服务总线与webHttpRelayBinding与REST/JSON和POST

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

如果你发送的是一些HTTP客户端而不是*HttpRelayBinding,并且你的中继端点需要客户端身份验证,那么你需要构建SAS令牌并将授权放入HTTP授权头。

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