Using IErrorHandler and TCP Message Security causes a timeout
I have a WCF service with a custom IServiceBehavior attached used to return a specific fault on the client side. When I enable this code with TCP Message Security I receive a service timeout.
Below you can see the full client and server code to reproduce the error.
Server code:
using System; usin开发者_如何学Pythong 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 } }
Client code:
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); } } }
If I remove the binding.Security.Mode = SecurityMode.Message;
line on the client and on the server the exception is correctly translated and the client can see it without problems.
On the WCF log I see these messages:
No signature message parts were specified for messages with the 'net.tcp://localhost:8184/fault' action. The security protocol cannot secure the outgoing message.Any idea on how to solve this problem? Seems that I must sign/encrypt the error message but I don't known how...
If I use Transport security the code works as expected.thanks!
It might be a bit late to respond but I had the same issue and I figured it out:
You need to define FaultContract
on your interface methods along with OperationContract
. Have a look here.
Also your fault contract object needs to have the same namespace as the Action namespace.
Sooooooooooo painful but finally figured out and works now.
In my case the problem was in the namespace. I have replaced the ProvideFault method with this:
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);
}
Note the null
parameter.
Consider also that with the wrong namespace the exception is not correctly serialized.
精彩评论