WCF进阶:将消息正文Base64编码

   大家好,很久没有写博文了,平时也是在用WCF做着项目,但不写文,总是感觉对技术的把握不够清楚全面。同时更主要的是和大家缺少很多沟通,有愧疚感呀。好了,闲话少叙,从今天起,我将推出WCF进阶系列博文。和大家一起来继续学习WCF。欢迎板砖!

   WCF有一套标准的安全机制,但使用过的朋友都清楚,这些机制在编程和配置上都非常麻烦,重要的是还需要证书,这也不太容易部署,本文讲述如何通过自定义Formatter将消息正文Base64编码传输,虽然安全级别不是太高,但也能够防止消息正文已明文形式传输。保护消息的秘密性。而且使用起来非常方便。通过学习本文,您能了解如下内容:1) 如何自定义消息记录 2) 如何修改消息 3) 如何实现自定义编码器。在WCF中消息是在通道堆栈中传递的,每个通道都可以对消息进行加工处理,上一个通道的输出为下一个通道的输入,而在消息通道中,WCF还开放很多Inspector,比如MessageInspector和ParamterInspector,通过这些拦截器,我们能实现自定义的消息记录和修改。WCF还提供了自定义Formatter的接口来实现自定义的编码器。我们就是利用WCF的上述接口来自定义编码,将消息正文编码为Base64的字符串,并且通过自定义的MessageInspector来记录消息内容。

先看一下如何实现自定义MessageInspector,在WCF中提供了两个接口: IClientMessageInspector和IDispatchMessageInspector,分别用于截取客户端消息和服务端消息。这两个接口的定义如下:

public interface IClientMessageInspector

{

// Methods

void AfterReceiveReply(ref Message reply, object correlationState);

object BeforeSendRequest(ref Message request, IClientChannel channel);

} 

 

public interface IDispatchMessageInspector

{

// Methods

object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext);

void BeforeSendReply(ref Message reply, object correlationState);

} 

 

实现了上述两个接口的类即是自定义的MessageInspector。客户端ClientRuntime对象和服务端的DispatchRumtime对象中管理中MessageInspectors集合,我们将自定义的MessageInspector实例化对象添加到集合中,MessageInspector即可生效。这个过程需要实现自定义的IEndpointBehavior。该接口的定义为:

public interface IEndpointBehavior

{

// Methods

void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters);

void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime);

void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher);

void Validate(ServiceEndpoint endpoint);

} 

 

下面有两个个代码片段,分别实现了自定义MessageInspector和EndpointBehavior

1 . 自定义的MessageInspector:

 

 

ExpandedBlockStart.gif 代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.ServiceModel.Dispatcher;

namespace  RobinLib
{
    
public   class  MessageInspector : IClientMessageInspector,IDispatchMessageInspector
    {
        
#region  IClientMessageInspector 成员

        
public   void  AfterReceiveReply( ref  System.ServiceModel.Channels.Message reply,  object  correlationState)
        {
            Console.WriteLine(reply.ToString());
        }

        
public   object  BeforeSendRequest( ref  System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
        {
            Console.WriteLine(request.ToString());
            
return   null ;
        }

        
#endregion

        
#region  IDispatchMessageInspector 成员

        
public   object  AfterReceiveRequest( ref  System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
        {
            Console.WriteLine(request.ToString());
            
return   null ;
        }

        
public   void  BeforeSendReply( ref  System.ServiceModel.Channels.Message reply,  object  correlationState)
        {
            Console.WriteLine(reply.ToString());
        }

        
#endregion
    }
}

 

 

2 . 用于自定义消息记录的自定义EndpointBehavior

 

ExpandedBlockStart.gif 代码
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.ServiceModel.Description;
using  System.ServiceModel.Dispatcher;

namespace  RobinLib
{
    
public   class  OutputMessageBehavior : IEndpointBehavior
    {

        
#region  IEndpointBehavior 成员

        
public   void  AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {

        }

        
public   void  ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(
new  MessageInspector());
        }

        
public   void  ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(
new  MessageInspector());
        }

        
public   void  Validate(ServiceEndpoint endpoint)
        {

        }

        
#endregion
    }
}

 

 

实现了上述两个类,我们就基本搞定了第一个问题:如何自定义消息记录,下面我们来探讨如何实现自定义Formatter。WCF提供了IClientMessageFormatter和IDispatchMessageFormatter两个接口类分别用于客户端和服务端的编解码。

 

public interface IClientMessageFormatter

{

// Methods

object DeserializeReply(Message message, object[] parameters);

Message SerializeRequest(MessageVersion messageVersion, object[] parameters);

} 

 

public interface IDispatchMessageFormatter

{

// Methods

void DeserializeRequest(Message message, object[] parameters);

Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result);

} 

 

实现这两个接口,就实现了自定义的编码器,但说句实在的,要实现完整通用的编码器并不是容易的事情。偷偷通过.Net Reflactor查看了下.Net Framework源码,缺省的实现代码量至少也有上千行了。我这次实现自定义的Formatter,其实是修改缺省Formatter编码好的消息,将消息的正文<S:Body></S:Body>中的内容转变成Base64字符串,然后在编码到另外的消息中的形式来实现自定义消息编码的。这个过程有三个主要的难点:

  1. 如何创建自定义的Message对象

 

  1. 如何将原始消息转换为Base64正文的消息
  2. 如何从Base64正文消息转换为原始消息。

 

我通过三个函数,分别实现了这三个难点:

 

1. 自定义的BodyWriter 

 

ExpandedBlockStart.gif 代码
using  System; 

using  System.Collections.Generic; 

using  System.Linq; 

using  System.Text; 

using  System.ServiceModel.Channels; 

using  System.Xml; 

 

namespace  RobinLib 



public   class  MyBodyWriter : BodyWriter 



string  body  =   ""

XmlDocument doc; 

public  MyBodyWriter( string  body) 

base ( true



this .body  =  body; 



 

public  MyBodyWriter(XmlDocument doc) 

base ( true



this .doc  =  doc; 



 

protected   override   void  OnWriteBodyContents(XmlDictionaryWriter writer) 



XmlWriterSettings setting 
=   new  XmlWriterSettings(); 

setting.NewLineHandling 
=  NewLineHandling.Entitize; 

setting.CheckCharacters 
=   false

if  ( ! string .IsNullOrEmpty(body)) 



writer.WriteRaw(body); 



if  (doc  !=   null



doc.WriteContentTo(writer); 

writer.Flush(); 









 

2. 转换原始消息为Base64正文的消息

 

ExpandedBlockStart.gif 代码
private  Message Base64BodyMessage(Message ora_msg) 



MemoryStream ms 
=   new  MemoryStream(); 

XmlDictionaryWriter writer 
=  XmlDictionaryWriter.CreateTextWriter(ms, Encoding.UTF8); 

ora_msg.WriteBodyContents(writer); 

writer.Flush(); 

string  body  =  System.Text.Encoding.UTF8.GetString(ms.GetBuffer()); 

byte [] buffer  =  System.Text.Encoding.UTF8.GetBytes(body); 

body 
=  Convert.ToBase64String(buffer); 

Message msg 
=  Message.CreateMessage(ora_msg.Version, ora_msg.Headers.Action,  new  MyBodyWriter(body)); 

ora_msg.Close(); 

return  msg; 



 

 

3. 转换Base64消息为原始消息

 

 

ExpandedBlockStart.gif 代码
private  Message RestoreMessageFormBase64Message(Message message) 



Message msg 
=   null

MemoryStream ms 
=   new  MemoryStream(); 

XmlDictionaryWriter writer 
=  XmlDictionaryWriter.CreateTextWriter(ms, Encoding.UTF8); 

message.WriteBody(writer); 

writer.Flush(); 

string  body  =  System.Text.Encoding.UTF8.GetString(ms.GetBuffer()); 

int  index  =  body.IndexOf( " > " ); 

int  index2  =  body.IndexOf( " </ " ); 

body 
=  body.Substring(index  +   1 , index2  -  index  -   1 ); 

byte [] buffer2  =  Convert.FromBase64String(body); 

body 
=  System.Text.Encoding.UTF8.GetString(buffer2); 

XmlDocument doc 
=   new  XmlDocument(); 

doc.LoadXml(body); 

msg 
=  Message.CreateMessage(message.Version, message.Headers.Action,  new  MyBodyWriter(doc)); 

msg.Headers.FaultTo 
=  message.Headers.FaultTo; 

msg.Headers.From 
=  message.Headers.From; 

msg.Headers.MessageId 
=  message.Headers.MessageId; 

msg.Headers.RelatesTo 
=  message.Headers.RelatesTo; 

msg.Headers.ReplyTo 
=  message.Headers.ReplyTo; 

msg.Headers.To 
=  message.Headers.To; 

foreach  (var mh  in  message.Headers) 



if  (mh  is  MessageHeader) 



if  (mh.Name  !=   " Action "   &&  mh.Name  !=   " FaultTo "   &&  mh.Name  !=   " From "   &&  mh.Name  !=   " MessageID "   &&  mh.Name  !=   " RelatesTo "   &&  mh.Name  !=   " ReplyTo "   &&  mh.Name  !=   " To "



msg.Headers.Add(mh 
as  MessageHeader); 







msg.Properties.Clear(); 

foreach  (var p  in  message.Properties) 



msg.Properties[p.Key] 
=  p.Value; 



return  msg; 



 

完整的代码见附件。实现了自定义的Formatter,我们还需要通过扩展OperationBehavior,将应用Formatter,因此我们创建了Base64BodyBehavior,代码为:

 

ExpandedBlockStart.gif 代码
using  System; 

using  System.Collections.Generic; 

using  System.Linq; 

using  System.Text; 

using  System.ServiceModel.Description; 

 

namespace  RobinLib 



///   <summary>  

///  将消息主题序列为base64的OperationBehavior 

///   </summary>  

public   class  Base64BodyBehavior : IOperationBehavior 



#region  IOperationBehavior 成员 

 

public   void  AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 



 



 

public   void  ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation) 



clientOperation.SerializeRequest 
=   true

clientOperation.DeserializeReply 
=   true

clientOperation.Formatter 
=   new  Base64BodyFormatter(clientOperation.Formatter); 



 

public   void  ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation) 



dispatchOperation.DeserializeRequest 
=   true

dispatchOperation.SerializeReply 
=   true

dispatchOperation.Formatter 
=   new  Base64BodyFormatter(dispatchOperation.Formatter); 



 

public   void  Validate(OperationDescription operationDescription) 



 



 

#endregion  





 

 

好了,目前我们已经将自定义消息记录和自定义编码器基本实现了,现在就涉及到如何在服务器代码和客户端代码中添加EndpointBehavior和OperationBehavior,我们用纯代码的形式做了一下实现, 

 

1. 在服务器托管代码中:

 

 

ExpandedBlockStart.gif 代码
using  System; 

using  System.Collections.Generic; 

using  System.Linq; 

using  System.Text; 

using  System.ServiceModel; 

 

namespace  Robin_Wcf_Host_Console 



class  Program 



static   void  Main( string [] args) 



// 服务地址 

Uri baseAddress 
=   new  Uri( " net.tcp://127.0.0.1:8081/Robin_Wcf_Formatter " ); 

ServiceHost host 
=   new  ServiceHost( typeof (Robin_Wcf_SvcLib.Service1),  new  Uri[] { baseAddress }); 

// 服务绑定 

NetTcpBinding bind 
=   new  NetTcpBinding(); 

host.AddServiceEndpoint(
typeof (Robin_Wcf_SvcLib.IService1), bind,  "" ); 

if  (host.Description.Behaviors.Find < System.ServiceModel.Description.ServiceMetadataBehavior > ()  ==   null



System.ServiceModel.Description.ServiceMetadataBehavior svcMetaBehavior 
=   new  System.ServiceModel.Description.ServiceMetadataBehavior(); 

svcMetaBehavior.HttpGetEnabled 
=   true

svcMetaBehavior.HttpGetUrl 
=   new  Uri( " http://127.0.0.1:8001/Mex " ); 

host.Description.Behaviors.Add(svcMetaBehavior); 



host.Opened
+= new  EventHandler( delegate ( object  obj,EventArgs e){ 

Console.WriteLine(
" 服务已经启动! " ); 

}); 

foreach  (var sep  in  host.Description.Endpoints) 



sep.Behaviors.Add(
new  RobinLib.OutputMessageBehavior()); 

foreach  (var op  in  sep.Contract.Operations) 



op.Behaviors.Add(
new  RobinLib.Base64BodyBehavior()); 





host.Open(); 

Console.Read(); 







 

 

2.  在客户端代理代码中:

 

 

ExpandedBlockStart.gif 代码
[System.Diagnostics.DebuggerStepThroughAttribute()] 

[System.CodeDom.Compiler.GeneratedCodeAttribute(
" System.ServiceModel " " 3.0.0.0 " )] 

public   partial   class  Service1Client : System.ServiceModel.ClientBase < Robin_Wcf_ClientApp.Robin_Wcf_Formatter_Svc.IService1 > , Robin_Wcf_ClientApp.Robin_Wcf_Formatter_Svc.IService1 



 

public  Service1Client() 



base .Endpoint.Behaviors.Add( new  RobinLib.OutputMessageBehavior()); 

foreach  (var op  in   base .Endpoint.Contract.Operations) 



op.Behaviors.Add(
new  RobinLib.Base64BodyBehavior()); 





 

public  Service1Client( string  endpointConfigurationName) : 

base (endpointConfigurationName) 



base .Endpoint.Behaviors.Add( new  RobinLib.OutputMessageBehavior()); 

foreach  (var op  in   base .Endpoint.Contract.Operations) 



op.Behaviors.Add(
new  RobinLib.Base64BodyBehavior()); 





 

public  Service1Client( string  endpointConfigurationName,  string  remoteAddress) : 

base (endpointConfigurationName, remoteAddress) 



base .Endpoint.Behaviors.Add( new  RobinLib.OutputMessageBehavior()); 

foreach  (var op  in   base .Endpoint.Contract.Operations) 



op.Behaviors.Add(
new  RobinLib.Base64BodyBehavior()); 





 

public  Service1Client( string  endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 

base (endpointConfigurationName, remoteAddress) 



base .Endpoint.Behaviors.Add( new  RobinLib.OutputMessageBehavior()); 

foreach  (var op  in   base .Endpoint.Contract.Operations) 



op.Behaviors.Add(
new  RobinLib.Base64BodyBehavior()); 





 

public  Service1Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 

base (binding, remoteAddress) 



base .Endpoint.Behaviors.Add( new  RobinLib.OutputMessageBehavior()); 

foreach  (var op  in   base .Endpoint.Contract.Operations) 



op.Behaviors.Add(
new  RobinLib.Base64BodyBehavior()); 





 

public   string  GetData( int  value) 



return   base .Channel.GetData(value); 



 

public  Robin_Wcf_ClientApp.Robin_Wcf_Formatter_Svc.CompositeType GetDataUsingDataContract(Robin_Wcf_ClientApp.Robin_Wcf_Formatter_Svc.CompositeType composite) 



return   base .Channel.GetDataUsingDataContract(composite); 





 

 

最后,我们比较一下记录到的原始消息和经过Base64 正文消息的对比

 

客户端请求原始消息

客户端Base64正文后消息:

 

服务器端原始消息:

 

 

服务端Base64正文后消息:

 

 

想要学习WCF的一些基础知识可以访问如下链接:

 

   

   

WCF从理论到实践一:揭开神秘面纱

 

   

WCF从理论到实践二:决战紫禁之巅

 

   

WCF从理论到实践三:八号当铺之黑色契约

 

   

WCF从理论到实践四:路在何方

 

   

WCF从理论到实践:Binding细解

 

   

WCF从理论到实践:WCF架构

 

   

WCF从理论到实践:消息交换模式

 

   

WCF从理论到实践:事件广播

 

   

WCF从理论到实践:实例模式和对象生命周期

 

   

WCF从理论到实践:异常处理

 

   

WCF从理论到实践-异步

 

   

WCF从理论到实践:事务

 

   

WCF从理论到实践:事务投票

 

   

WCF从理论到实践(14):WCF解决方案模板

 

   

WCF从理论到实践(15):响应变化

 

   

WCF从理论到实践(16):操作重载(带视频+ppt+源码)

 

   

WCF从理论到实践(17):OO大背离(带视频+ppt+源码)

 

   

Ajax访问Xml Web Service的安全问题以及解决方案

 

   

AjaxWCF交互-WCF之美

 

   

AjaxWcf交互-JSON

 

   

ExtJsWCF交互:生成树

 

   

ExtJs+Linq+Wcf打造简单grid

 
 

ExtJs+WCF+LINQ实现分页Grid

 

   

ExtJsWCF之间的跨域访问

 

   

异步调用RestfulWCF服务

 

   

利用WCFRestful进行上传下载

  

项目文件: /Files/jillzhang/Robin_Wcf_Formatter.rar

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值