在当前的项目中,使用WCF来实现从互联网客户端到 Windows 服务的通信。WCF 能够自动地让 Windows 身份验证信息从客户端流向服务器,这非常便利。然而,客户端的文化信息(例如语言和地区设置)并没有被传递,这导致在读取本地化数据(例如资源文件)时,应用无法透明地处理本地化。本文将展示如何通过实现自定义的 WCF 消息检查器(MessageInspector)来解决这个问题。
这种扩展只应在控制通信两端(客户端和服务器)的情况下使用。如果只创建服务器并关心互操作性,最好使用更标准的方法,如 Pablo Cibraro 在《WCF的全球化模式(WS-I18N 实现)》中描述的那样。
解决方案分为四个项目:服务器、客户端、扩展和服务接口。其中,扩展项目是最有趣的部分。在示例中,客户端设置了当前线程的文化信息,并调用服务器上的一个简单的 "Hello World" 方法。服务器响应时,输出当前线程的文化信息。
这是自定义的消息检查器,其中客户端的 BeforeSendRequest
方法添加了包含线程文化信息的消息头,服务器端的 AfterReceiveRequest
方法提取自定义消息头并设置当前执行线程的文化信息。
public class CultureMessageInspector : IClientMessageInspector, IDispatchMessageInspector
{
private const string HeaderKey = "culture";
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader(HeaderKey, string.Empty, Thread.CurrentThread.CurrentUICulture.Name));
return null;
}
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
int headerIndex = request.Headers.FindHeader(HeaderKey, string.Empty);
if (headerIndex != -1)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(request.Headers.GetHeader(headerIndex));
}
return null;
}
}
自定义行为的创建如下:
public class CultureBehaviour : IEndpointBehavior
{
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new CultureMessageInspector());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CultureMessageInspector());
}
}
创建 ServiceHost
时,将自定义行为添加到 ServiceEndPoint
的行为集合中。这也可以配置在配置文件中。
Server server = new Server();
ServiceHost host = new ServiceHost(server, new Uri("net.tcp://localhost:8080"));
NetTcpBinding binding = new NetTcpBinding();
ServiceEndpoint endPoint = host.AddServiceEndpoint(typeof(IHelloWorld), binding, "Server");
endPoint.Behaviors.Add(new CultureBehaviour());
创建客户端通道时,也将自定义行为添加到 ServiceEndPoint
的行为集合中。这也可以配置在配置文件中。
ServiceEndpoint tcpEndPoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(IHelloWorld)), new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8080/server"));
ChannelFactory factory = new ChannelFactory(tcpEndPoint);
factory.Endpoint.Behaviors.Add(new CultureBehaviour());
return factory.CreateChannel();
不确定运行 AfterReceiveRequest
的线程是否与运行实际服务器代码的线程相同。在这种情况下,它们是相同的。如果需要在 WCF 管道的其他部分使用消息头中的数据,应该将数据添加到请求的属性集合中。