使用 IErrorHandler 和 TCP Message Security 导致超时
我有一个附加了自定义 IServiceBehavior 的 WCF 服务,用于在客户端返回特定错误。当我使用 TCP Message Security 启用此代码时,我收到服务超时。
您可以在下面看到重现错误的完整客户端和服务器代码。
服务器代码:
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 } }
客户端代码:
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); } } }
如果我删除客户端和服务器上的 binding.Security.Mode = SecurityMode.Message;
行,则异常将被正确转换,并且客户端可以毫无问题地看到它。
在 WCF 日志上我看到以下消息:
未为具有“net.tcp://localhost:8184/fault”操作的消息指定签名消息部分。
安全协议无法保护传出消息的安全。
关于如何解决这个问题有什么想法吗?似乎我必须签署/加密错误消息,但我不知道如何...
如果我使用传输安全性,代码将按预期工作。
谢谢!
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; 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 } }
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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
回复可能有点晚了,但我遇到了同样的问题,我想通了:
您需要在接口方法上定义
FaultContract
以及OperationContract
。请查看此处。此外,您的故障契约对象需要具有与 Action 命名空间相同的命名空间。
太痛苦了,但现在终于弄清楚并且可以工作了。
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 withOperationContract
. 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.
就我而言,问题出在命名空间中。我已将 ProvideFault 方法替换为以下内容:
注意
null
参数。还要考虑使用错误的命名空间,异常不会正确序列化。
In my case the problem was in the namespace. I have replaced the ProvideFault method with this:
Note the
null
parameter.Consider also that with the wrong namespace the exception is not correctly serialized.