65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在消息对象中传递任意数据(使用 WCF)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2009年3月31日

Ms-PL

2分钟阅读

viewsIcon

37693

如何在使用 WCF 的消息对象中传递任意数据

我在互联网上搜索了很久,但仍然找不到关于这个看似简单的任务的任何信息。假设你有一个任意的 string,你想从 WCF(Windows Communication Foundation)服务返回它。

你可以在服务契约中执行以下操作之一:

  1. 创建一个返回类型为 string 的方法/操作。
  2. 创建一个返回类型为 Stream 的方法/操作,以便完全控制序列化的执行方式。你可以在 此链接 找到一个很好的解释。

但是,你可能会发现自己处于无法返回 Stream 类型的情况,因为你将响应发送到 WCF 堆栈的深处,并且你只有 System.ServiceModel.Message 类型可以使用。 这是我所说的 Message 类的文档

补充说明:如果你曾经使用过 RIA(富互联网应用程序),例如 Flash 或 Silverlight,你很可能熟悉跨域策略文件,例如 crossdomain.xml(Flash)或 clientaccesspolicy.xml(Silverlight)。我不会深入探讨它们为什么需要,但总而言之,我需要根据客户端发出的请求返回其中一个文件。这两个文件都包含有效的 XML。

如果你仔细查看 Message.CreateMessage 方法,你会看到它有很多重载。最初,我尝试了以下方法:

Message reply = Message.CreateMessage(MessageVersion.None, "", 
XmlReader.Create(new MemoryStream(Encoding.UTF8.GetBytes("[XML goes here]"))));

只要传递给 XmlReader 的 XML 不包含 DTD 定义,这就可以正常工作。出于某种安全原因,XML 中包含 DTD 会导致 XmlReader.Create 调用时发生异常。我可以通过将 XmlReaderSettings 上的 ProhibitDtd 属性设置为 false 来避免异常,但 WCF 决定我正在做邪恶的事情,并独立决定在没有 DTD 定义的情况下发出我的 XML。

如果你查看 Message.CreateMessage 的重载,你很快就会意识到你必须将其序列化为 XML。在我的例子中,crossdomain.xml 中的 DTD 导致了头痛,但是如果你想按原样返回一个任意的 string,例如“Hello World”,该怎么办?你尝试的任何操作都会导致默认的 DataContractSerializer 启动并序列化你的对象。关键在于,你实际上并不想要序列化

这是使用 WCF 的 Message 类返回任意文本所需的代码。

/// <summary>
/// Necessary to write out the contents as text (used with the Raw return type)
/// </summary>
public class TextBodyWriter : BodyWriter
{
    byte[] messageBytes; 

    public TextBodyWriter(string message)
        : base(true)
    {
        this.messageBytes = Encoding.UTF8.GetBytes(message);
    } 

    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        writer.WriteStartElement("Binary");
        writer.WriteBase64(this.messageBytes, 0, this.messageBytes.Length);
        writer.WriteEndElement();
    }
} 

// .... 

// Here is sample code that does the magic of returning raw text to the client...
string response = "Hello World!";
Message reply = Message.CreateMessage(MessageVersion.None, null, 
    new TextBodyWriter(response));
// very important - forces WCF to serialize the message body contents as
// "Raw" which effectively means no serialization
reply.Properties[WebBodyFormatMessageProperty.Name] = new WebBodyFormatMessageProperty(
    WebContentFormat.Raw); 
// can also be "application/xml" or anything you prefer 
WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
© . All rights reserved.