WCF请求正文有时是流,有时是缓冲?

4

我有一个 WCF webservice,我想通过实现 MessageInspector 并在 AfterReceiveRequest() 事件中记录日志来记录每个请求。

但是当我使用 SOAPUI 作为客户端向 webservice 发送请求时,复制请求消息会导致消息体仅显示 <body>... stream ...</body> ,并且无法在后续的验证中以 XML 文档的形式加载。而当我使用 WCFTestClient.exe 发送请求时,一切正常,消息被记录,请求继续进行。

我的猜测是,WCFTestClient.exe 发送的请求是带有缓冲消息体的,而来自 SOAPUI 的请求则是流式消息体?这可能吗?

  • 是否有一种安全的方式可以编写代码,以安全地复制任一版本的消息?我尚未弄清楚如何安全地复制流式版本,因为 CreateBufferedCopy() 显然无法达到此目的。

  • 或者,我可以配置 WCF,始终创建缓冲消息体而不是流式消息体吗?

以下是我用于记录和复制请求消息的代码:

object IDispatchMessageInspector.AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
    try
    {

        MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
        request = buffer.CreateMessage();

        Message copy = buffer.CreateMessage();

        LogRequest(copy);

        ValidateMessage(ref request);
    }
    catch (Exception e)
    {
        throw new FaultException<>()...
    }
    return null;
}

如果请求消息来自SOAPUI并具有流式正文,则在ValidateMessage()方法中该消息的副本无法加载到XML文档中。如果请求消息来自WCFTestClient.exe并具有缓冲正文,则可以将其加载为XML文档。

void validateMessage(ref System.ServiceModel.Channels.Message message)
{
    XmlDocument bodyDoc = new XmlDocument();
    //This load throws exception if request came from SOAPUI with streamed body...
    bodyDoc.Load(message.GetReaderAtBodyContents());
    ...
}

Load()方法抛出的异常是:

System.InvalidOperationException {"指定的节点不能作为此节点的有效子级插入,因为指定的节点类型错误。"}

at System.Xml.XmlDocument.AppendChildForLoad(XmlNode newChild, XmlDocument doc) at System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc) at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace) at System.Xml.XmlDocument.Load(XmlReader reader) at ...


1
你是如何在LogRequest方法中记录消息的? - mlessard
@mlessard 使用Microsoft Enterprise Library Logging框架。但似乎与此无关。如果我注释掉那行代码,仅将消息复制到“请求”变量中就会导致问题。 - TerryB
1
@TerryB:我的意思是,你在消息上使用ToString()了吗?我问这个是因为这不是读取消息的正确方式。ToString()通常不会返回正文内容,这可能是为什么你在正文中得到“...流…”的原因。请参阅msdn.microsoft.com/en-us/library/ms734675.aspx,第“访问消息正文以进行调试”部分。 - mlessard
也许是这样,但我认为这并不重要,对吧?如果我注释掉//LogRequest(),那么就不会在任何地方发生toString()。我复制的请求无法加载到XML文档中。而如果请求来自带有缓冲区体的WCFTestClient.exe,则可以很好地加载到xml文档中。我将更新问题以显示失败的代码。 - TerryB
3个回答

2

我相信SOAPUI始终将其构建的消息请求作为流发送。我不确定您是否可以通过SOAPUI测试中的代码或SOAPUI上的某些SOAPUI配置选项/文件来修改此设置。

请检查绑定的TransferMode属性,如此处此处所述。您可能有多个端点使用不同的自定义绑定,用于发送缓冲请求和流式请求的客户端。

希望这可以帮助您。


1

抛出的异常是什么?由GetReaderAtBodyContents()返回的读取器定位在正文内的第一个元素上,而不是在正文标记本身上。因此,您加载消息的方式是不正确的,因为正文可能包含多个节点,在这种情况下,它将失败。

只是为了检查,您可以使用以下代码验证整个消息(副本)的内容,并查看从SOAPUI发送时正文是否完全相同?


using (MemoryStream stream = new MemoryStream())
{
    using (XmlWriter writer = XmlWriter.Create(stream))
    {
        message.WriteMessage(writer);
        writer.Flush();
        stream.Position = 0;
    }
}

如果您想要获取主体内的所有节点,您可能需要自己创建一个 Body 节点。

嗨mlessard,我已经更新了问题并附上了异常抛出。 - TerryB
使用您的代码将每个请求写入文件的流。从SOAPUI发送的请求消息在复制之前与原来相同,也与从WCFClient发送的请求相同。因此,来自WCFClient的消息可以容忍被复制,并且仍然正常工作。而来自SOAPUI的消息如果被复制,则无法加载到xml文档中。如果没有被复制,则也没问题。我能够发现两种情况之间唯一的区别是SOAPUI中的一种情况在调试或使用ToString()时显示<body>...stream...</body>,而来自WCFClient的情况则没有。 - TerryB
1
我不知道SOAPUI,所以无法帮助你。但是,如果你使用ToString(),看到“...stream...”是正常的。ToString()并不总是返回正文内容,因此不要使用它进行调试。你的代码无法在XmlDocument中加载正文也是正常的,因为你传递给XmlDocument.Load()的是正文节点的内容,而不是正文节点本身。请参见“http://stackoverflow.com/questions/6091212/get-body-from-wcf-message”。这有帮助吗? - mlessard

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接