Using IErrorHandler and TCP Message Security causes a timeout
我有一个带有自定义ISeviceBehavior的WCF服务,用于返回客户端的特定错误。当我使用TCP消息安全性启用此代码时,将收到服务超时。
下面您可以看到完整的客户端和服务器代码来重现错误。
服务器代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.ServiceModel.Channels; namespace TestWCFServer { class Program { static void Main(string[] args) { Console.WriteLine("SERVER"); NetTcpBinding binding = new NetTcpBinding(); binding.Security.Mode = SecurityMode.Message; //If you remove this line the code works!!!! Uri address = new Uri("net.tcp://localhost:8184/"); // Create the ServiceHost. using (ServiceHost host = new ServiceHost(typeof(HelloWorldService))) { host.AddServiceEndpoint(typeof(IHelloWorldService), binding, address); host.Description.Behaviors.Add(new MyErrorhandlerBehavior()); host.Open(); Console.WriteLine("The service is ready at {0}", address); Console.WriteLine("Press to stop the service."); Console.ReadLine(); // Close the ServiceHost. host.Close(); } } } [ServiceContract] public interface IHelloWorldService { [OperationContract] string SayHello(string name); } public class HelloWorldService : IHelloWorldService { public string SayHello(string name) { if (name == null) throw new ArgumentNullException("name"); return string.Format("Hello, {0}", name); } } class MyErrorhandlerBehavior : IServiceBehavior, IErrorHandler { #region IServiceBahvior public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (ChannelDispatcher chanDisp in serviceHostBase.ChannelDispatchers) { chanDisp.ErrorHandlers.Add(this); } } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } #endregion #region IErrorHandler Members public bool HandleError(Exception error) { return true; } public void ProvideFault(Exception error,MessageVersion ver, ref Message msg) { FaultException fe = new FaultException(error.Message); MessageFault fault = fe.CreateMessageFault(); msg = Message.CreateMessage(ver, fault,"net.tcp://localhost:8184/fault"); } #endregion } } |
客户端代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; namespace TestWCFClient { class Program { static void Main(string[] args) { Console.WriteLine("CLIENT"); try { NetTcpBinding binding = new NetTcpBinding(); binding.Security.Mode = SecurityMode.Message; //If you remove this line the code works!!!! Uri address = new Uri("net.tcp://localhost:8184/"); EndpointAddress endpoint = new EndpointAddress(address); HelloWorldServiceClient client = new HelloWorldServiceClient(binding, endpoint); Console.WriteLine("Calling client with a valid parameter..."); Console.WriteLine(client.SayHello("Davide")); Console.WriteLine("OK"); Console.WriteLine("Calling client with an invalid parameter..."); Console.WriteLine(client.SayHello(null)); //This call causes the timeout when Security is set to Message Console.WriteLine("OK"); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine("Press enter to exit"); Console.ReadLine(); } } [ServiceContract] public interface IHelloWorldService { [OperationContract] string SayHello(string name); } class HelloWorldServiceClient : System.ServiceModel.ClientBase, IHelloWorldService { public HelloWorldServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress address) : base(binding, address) { } public string SayHello(string name) { return base.Channel.SayHello(name); } } } |
如果我在客户机和服务器上删除
在wcf日志中,我看到以下消息:没有为具有"net.tcp://localhost:8184/fault"操作的消息指定签名消息部分。安全协议无法保护传出消息。
你知道如何解决这个问题吗?似乎我必须签署/加密错误消息,但我不知道如何…如果我使用传输安全,代码将按预期工作。
谢谢!
回答可能有点晚,但我也有同样的问题,我发现了:
您需要在接口方法上定义
Soooooooooo很痛苦,但最终发现并开始工作。
在我的例子中,问题出现在名称空间中。我将providefault方法替换为:
1 2 3 4 5 6 | public void ProvideFault(Exception error,MessageVersion ver, ref Message { FaultException fe = new FaultException(error.Message); MessageFault fault = fe.CreateMessageFault(); msg = Message.CreateMessage(ver, fault, null); } |
注意
还应考虑使用错误的命名空间时,异常未正确序列化。