-1

以下文章介绍了如何使用 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 { 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();
        }
    }
}

我只需将设置参数更改为 http 或 tcp 即可使用 webHttpRelayBinding 或 netTcpRelayBiding

如前所述,使用 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 授权标头。

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 回答