zoukankan      html  css  js  c++  java
  • 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:

    代码
    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

    代码
    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 

    代码
    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正文的消息

    代码
    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消息为原始消息

    代码
    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,代码为:

    代码
    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. 在服务器托管代码中:

    代码
    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.  在客户端代理代码中:

    代码
    [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

    作者:jillzhang
    出处:http://jillzhang.cnblogs.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    BZOJ3420[POI2013]Triumphal arch&BZOJ5174[Jsoi2013]哈利波特与死亡圣器——树形DP+二分答案
    BZOJ3417[Poi2013]Tales of seafaring——BFS
    BZOJ3750[POI2015]Pieczęć——链表
    bzoj 3594 方伯伯的玉米田
    Cocos2dx学习之SimpleGame
    新旧版本的quick-x项目移植
    cocos2dx 2.2.1 下面创建新项目测试运行
    VS2012下面编译Cocos2dx的HelloLua项目时报错>>> 项目文件"" 已被重命名或已不在解决方案中
    cocos2dx-lua or quick 2dx 中的图片资源加密
    关于对quick-2dx项目中的Lua代码的加密
  • 原文地址:https://www.cnblogs.com/jillzhang/p/1709311.html
Copyright © 2011-2022 走看看