WCF建立在基于消息的通信这一概念基础上。通过方法调用(Method Call)形式体现的服务访问需要转化成具体的消息,并通过相应的编码(Encoding)才能通过传输通道发送到服务端;服务操作执行的结果也只能以消息的形式才能被正常地返回到客户端。所以,消息在整个WCF体系结构中处于一个核心的地位,WCF可以看成是一个消息处理的管道,如下图所示:
WCF的一个操作(以及操作的参数)被序列化为Soap协议所支持的消息(XML结构),经过服务运行层,交给Binding中所定义的消息传递层,消息传递层由通道(Channel)组成。通道是以某种方式对消息进行处理(例如通过对消息进行身份验证)的组件,通道对消息和消息头进行操作,而服务运行层主要针对消息正文内容进行处理。
方法一. 通过OperationContext直接添加/访问MessageHeader信息
使用OperationContext我们可以:访问当前操作执行环境。 特别是,操作上下文用于访问双工服务中的回调通道、存储整个操作部分的额外状态数据、访问传入消息头和属性以及添加传出消息头和属性。下面用代码演示下如何在MessageHeader中添加额外的信息,进行用户验证。
1. 服务契约
using System;
using System.Runtime.Serialization;
using System.ServiceModel;
namespace WcfSvcLib
{
[ServiceContract(Namespace="http://blog.csdn.net/fangxinggood")]
public interface IService1
{
[OperationContract]
string GetData(int value);
}
}
2. 服务实现
using System;
using System.Runtime.Serialization;
using System.ServiceModel;
namespace WcfSvcLib
{
public class Service1 : IService1
{
public string GetData(int value)
{
Console.WriteLine(OperationContext.Current.RequestContext.RequestMessage);
// 注意namespace必须和ServiceContract中定义的namespace保持一致,默认是:http://tempuri.org
var ns = "http://blog.csdn.net/fangxinggood";
var user = GetHeaderValue("user", ns);
var pwd = GetHeaderValue("pwd", ns);
// 验证失败会抛出Invalid User的异常。
if (user != "fangxing" || pwd != "password")
throw new FaultException("Invalid User!");
return string.Format("You entered: {0}", value);
}
private string GetHeaderValue(string name, string ns = "http://tempuri.org")
{
var headers = OperationContext.Current.IncomingMessageHeaders;
var index = headers.FindHeader(name, ns);
if (index > -1)
return headers.GetHeader<string>(index);
else
return null;
}
}
}
3. 客户端实现
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace WcfClient
{
class Program
{
static void Main(string[] args)
{
var client = new WcfSvc.Service1Client();
using (var scope = new OperationContextScope(client.InnerChannel))
{
// 注意namespace必须和ServiceContract中定义的namespace保持一致,默认是:http://tempuri.org
var myNamespace = "http://blog.csdn.net/fangxinggood";
// 注意Header的名字中不能出现空格,因为要作为Xml节点名。
var user = MessageHeader.CreateHeader("user", myNamespace, "fangxing");
var pwd = MessageHeader.CreateHeader("pwd", myNamespace, "password");
OperationContext.Current.OutgoingMessageHeaders.Add(user);
OperationContext.Current.OutgoingMessageHeaders.Add(pwd);
var result = client.GetData(100);
Console.WriteLine(result);
Console.Read();
}
}
}
}
运行一下,在服务端通过 Console.WriteLine(OperationContext.Current.RequestContext.RequestMessage); 输出了请求的Message。通过输出的信息,我们可以看到Header里添加的信息:
通过上面的代码,我们可以完成类似WebService的SoapHeader验证。但是这样需要我们每个契约都做类似的添加、验证,这样岂不是很繁琐。下面看方法二,通过消息检查器完成统一的用户验证。
方法二. 消息检查器方式添加/访问MessageHeader信息
客户端通过实现IClientMessageInspector接口,服务端通过实现IDispatchMessageInspector接口,来拦截消息。这种方式是通过扩展Behavior来加入拦截的,所以还需要分别实现IEndpointBehavior(客户端)和IServiceBehavior(服务端)接口,并通过配置将消息检查器加入。
工程结构: 实现说明:
【客户端】
1. ClientInterpector 实现:
using System;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Configuration;
using System.ServiceModel.Channels;
using System.ServiceModel;
namespace WcfClientInterpector
{
public class ClientInterpector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var userNameHeader = MessageHeader.CreateHeader("OperationUserName", "http://tempuri.org", "fangxing", false, "");
var pwdNameHeader = MessageHeader.CreateHeader("OperationPwd", "http://tempuri.org", "password", false, "");
request.Headers.Add(userNameHeader);
request.Headers.Add(pwdNameHeader);
Console.WriteLine(request);
return null;
}
}
}
2. MyClientBehavior 实现: (实现扩展endpointBehavior元素)
using System;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel;
namespace WcfClientInterpector
{
public class MyClientBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public override Type BehaviorType
{
get { return typeof(MyClientBehavior); }
}
protected override object CreateBehavior()
{
return new MyClientBehavior();
}
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new ClientInterpector());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
}
}
3. 配置
修改客户端配置文件,步骤如下:
(1) 在Advanced>Extensions>behavior element extensions中加入自定义的ClientInterpector。
(2) 在Advanced>Endpoint Behaviors中定义一个Behavior,添加上面配置过的extension
(3) 修改Client>Endpoints下的Endpoint的Behavior Config指向(2)配置的Behavior。
【服务端】
1. ServiceInterpector 实现:
using System;
using System.ServiceModel.Dispatcher;
using System.ServiceModel;
namespace WcfServiceInterpector
{
public class ServiceInterpector : IDispatchMessageInspector
{
#region IDispatchMessageInspector Members
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
Console.WriteLine(request);
var user = GetHeaderValue("OperationUserName");
var pwd = GetHeaderValue("OperationPwd");
if (user != "fangxing" || pwd != "password")
throw new FaultException("Invalid User!");
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
private string GetHeaderValue(string name, string ns = "http://tempuri.org")
{
var headers = OperationContext.Current.IncomingMessageHeaders;
var index = headers.FindHeader(name, ns);
if (index > -1)
return headers.GetHeader<string>(index);
else
return null;
}
#endregion
}
}
2. MyServiceBehavior 实现:(实现扩展serviceBehavior元素)
using System;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace WcfServiceInterpector
{
public class MyServiceBehavior : BehaviorExtensionElement, IServiceBehavior
{
public override Type BehaviorType
{
get { return typeof(MyServiceBehavior); }
}
protected override object CreateBehavior()
{
return new MyServiceBehavior();
}
#region IServiceBehavior Members
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher epDisp in chDisp.Endpoints)
{
epDisp.DispatchRuntime.MessageInspectors.Add(new ServiceInterpector());
}
}
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
#endregion
}
}
3. 配置
修改服务端配置文件,步骤如下:
(1) 在Advanced>Extensions>behavior element extensions中加入自定义的ServiceInterpector。
(2) 在Advanced>Service Behaviors中定义一个Behavior,添加上面配置过的extension
(3) 修改Services下的服务节点的Behavior Config指向(2)配置的Behavior。
源代码下载
本系列链接: