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

要对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消息头来验证,但是,上次的做法会有一个麻烦,那就是每次调用操作协定都要手动修改一次,这一次,我们直接在终结点级别进行修改和验证,就省去了许多功夫。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值