您的位置:首页 > 其它

WCF笔记(6)消息拦截与篡改

2013-03-24 16:23 471 查看
要对SOAP消息进行拦截和修改,我们需要实现两个接口,它们都位于System.ServiceModel.Dispatcher (程序集System.ServiceModel)。下面分别价绍。

接口一:IClientMessageInspector

从名字中我们可以猜测,它是用来拦截客户消息的,而看看它的方法,你就更加肯定当初的猜测了。

BeforeSendRequest:向服务器发送请求前拦截或修改消息(事前控制)
AfterReceiveReply:接收到服务器的回复消息后,在调用返回之前拦截或修改消息(事后诸葛亮)

接口二:IDispatchMessageInspector

刚才那个接口是针对客户端的,而这个是针对服务器端的。

AfterReceiveRequest:接收客户端请求后,在进入操作处理代码之前拦截或修改消息(欺上)
BeforeSendReply:服务器向客户端发送回复消息之前拦截和修改消息(瞒下)。

虽然实现了这两个接口,但你会有新的疑问,怎么用?把它们放到哪儿才能拦截消息?因此,下一步就是要实现IEndpointBehavior按口(System.ServiceModel.Description命名空间,程序集System.ServiceModel),它有四个方法,而我们只需要处理两个就够了。

下面是MSDN的翻译版本说明:

使用 ApplyClientBehavior 方法可以在客户端应用程序中修改、检查或插入对终结点中的扩展。

使用 ApplyDispatchBehavior 方法可以在服务应用程序中修改、检查或插入对终结点范围执行的扩展。
我想不用额外解释了,说白了就是一个在客户拦截和修改消息,另一个在服务器端拦截和修改消息。
在实现这两个方法时,和前面我们实现的IClientMessageInspector和IDispatchMessageInspector联系起来就OK了。
做完了IEndpointBehavior的事情后,把它插入到服务终结点中就行了,无论是服务器端还是客户端,这一步都必须的,因为我们实现的拦截器是包括两个端的,因此,较好的做法是把这些类写到一个独立的类库(dll)中,这样一来,服务器端和客户端都可以引用它。

示例代码

1、实现消息拦截器

using System;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;

namespace MyMessageIntercept
{
/// <summary>
/// 消息拦截器【实现客户端和服务端的消息拦截】
/// </summary>
public class MessageIntercept:IClientMessageInspector,IDispatchMessageInspector
{
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
Console.WriteLine("客户端接收到的回复:{0}\n", reply);
}

public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
Console.WriteLine("客户端发送请求前的SOAP消息:{0}\n", request);
return null;
}

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
Console.WriteLine("服务器端接收到的请求:{0}\n", request);
return null;
}

public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
Console.WriteLine("服务器将作出以下回复:{0}\n", reply);
}
}

/// <summary>
/// 将自定义的消息拦截器(MessageIntercept)插入到终结点行为
/// </summary>
public class MyEndPointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
//不需要
}

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
//植入"窃听器"客户端
clientRuntime.ClientMessageInspectors.Add(new MessageIntercept());
}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
//植入"窃听器"服务端
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MessageIntercept());
}

public void Validate(ServiceEndpoint endpoint)
{
//不需要
}
}
}


2、实现服务端

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Runtime.Serialization;
using MyMessageIntercept;

namespace Server
{
class Program
{
//设置服务并启动
static void Main()
{
//服务基地址
Uri baseUri = new Uri("http://localhost:3000/Service");
//声明服务器主机
using (ServiceHost host = new ServiceHost(typeof(MyService), baseUri))
{
//添加绑定和终结点
WSHttpBinding binding = new WSHttpBinding();
host.AddServiceEndpoint(typeof(IService), binding, "test");
//添加服务描述
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
//把自定义的"MyEndPointBehavior"插入到终结点中
foreach (var endPoint in host.Description.Endpoints)
{
endPoint.EndpointBehaviors.Add(new MyEndPointBehavior());
}
//启动服务
try
{
host.Open();
Console.WriteLine("服务已启动");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
//关闭服务
host.Close();
}
}
}

/// <summary>
/// 定义服务协定
/// </summary>
[ServiceContract]
public interface IService
{
[OperationContract]
int AddInt(int a, int b);
[OperationContract]
Student GetStudent();
[OperationContract]
CalculateResultResponse CaculateNumber(CalculateRequest request);
}

/// <summary>
/// 实现服务协定
/// </summary>
public class MyService : IService
{
public int AddInt(int a, int b)
{
return a + b;
}

public Student GetStudent()
{
Student st = new Student
{
Name = "小明",
Age = 23
};
return st;
}

public CalculateResultResponse CaculateNumber(CalculateRequest request)
{
CalculateResultResponse result = new CalculateResultResponse();
switch (request.Operation)
{
case "+":
result.CalculateResult = request.NumberA + request.NumberB;
break;
case "-":
result.CalculateResult = request.NumberA - request.NumberB;
break;
case "*":
result.CalculateResult = request.NumberA * request.NumberB;
break;
case "/":
result.CalculateResult = request.NumberA / request.NumberB;
break;
default:
throw new ArgumentException("运算操作符只允许'+'、'-'、'*'、'/'.");
}
return result;
}

}

[DataContract]
public class Student
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}

[MessageContract]
public class CalculateRequest
{
/// <summary>
/// 操作符
/// </summary>
[MessageHeader]
public string Operation { get; set; }

/// <summary>
/// 第一个数
/// </summary>
[MessageBodyMember]
public int NumberA { get; set; }

/// <summary>
/// 第二个数
/// </summary>
[MessageBodyMember ]
public int NumberB { get; set; }
}

/// <summary>
/// 计算返回的结果
/// </summary>
[MessageContract]
public class CalculateResultResponse
{
/// <summary>
/// 计算结果
/// </summary>
[MessageBodyMember]
public int CalculateResult { get; set; }
}
}


3、实现客户端

using System;
using MyMessageIntercept;

namespace Client
{
class Program
{
private static void Main()
{
WS.ServiceClient sc = new WS.ServiceClient();
//记得在客户端也要插入自定义的"MyEndPointBehavior"
sc.Endpoint.EndpointBehaviors.Add(new MyEndPointBehavior());
try
{
// 1、调用带元数据参数和返回值的操作
Console.WriteLine("10和55相加的结果是:{0}\n", sc.AddInt(10, 55));
//2、调用带有数据协定的操作
WS.Student st = sc.GetStudent();
Console.WriteLine("-----学生信息--------\n姓名:{0}\n年龄:{1}\n", st.Name, st.Age);
//3、调用带有消息协定的操作
Console.WriteLine("12乘以56的结果是:{0}\n", sc.CaculateNumber("*", 12, 56));
sc.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}


消息拦截效果图





修改消息,只需要对消息拦截器(MessageIntercept)类做修改

using System;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;

namespace MyMessageIntercept
{
/// <summary>
/// 消息拦截器【实现客户端和服务端的消息拦截】
/// </summary>
public class MessageIntercept:IClientMessageInspector,IDispatchMessageInspector
{
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
//Console.WriteLine("客户端接收到的回复:\n{0}", reply);
}

public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
//Console.WriteLine("客户端发送请求前的SOAP消息:\n{0}", request);
// (客服端)插入验证信息
System.ServiceModel.Channels.MessageHeader hdUserName = System.ServiceModel.Channels.MessageHeader.CreateHeader("user", "myTest", "admin");
System.ServiceModel.Channels.MessageHeader hdPassWord = System.ServiceModel.Channels.MessageHeader.CreateHeader("pw", "myTest", "123");
request.Headers.Add(hdUserName);
request.Headers.Add(hdPassWord);
return null;
}

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
//Console.WriteLine("服务器端:接收到的请求:\n{0}", request);
// (服务端)栓查验证信息
string un = request.Headers.GetHeader<string>("user", "myTest");
string ps = request.Headers.GetHeader<string>("pw", "myTest");
if (un == "admin" && ps == "123")
{
Console.WriteLine("用户名和密码正确.");
}
else
{
throw new Exception("验证失败!");
}
return null;
}

public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
//Console.WriteLine("服务器即将作出以下回复:\n{0}", reply);
}
}

/// <summary>
/// 将自定义的消息拦截器(MessageIntercept)插入到终结点行为
/// </summary>
public class MyEndPointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
//不需要
}

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
//植入"窃听器"客户端
clientRuntime.ClientMessageInspectors.Add(new MessageIntercept());
}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
//植入"窃听器"服务端
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MessageIntercept());
}

public void Validate(ServiceEndpoint endpoint)
{
//不需要
}
}
}
注意:添加对System.Runtime.Serialization的引用。

前面我们说过,如果安装证书进行身份验证会相当麻烦,而可以通过修改SOAP消息头来验证,但是,上次的做法会有一个麻烦,那就是每次调用操作协定都要手动修改一次,这一次,我们直接在终结点级别进行修改和验证,就省去了许多功夫。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: