首页 > 解决方案 > WCF 消息检查器 - 修改无效的 XML SOAP 响应

问题描述

我在 .NET 中使用 WCF 客户端调用 SOAP 服务。有时,SOAP 服务会发回无法序列化到 .NET 对象的响应。这里的罪魁祸首是一个日期时间字段,他们在其中发回“24:00:00”而不是“00:00:00”。

在这种情况下,我们返回以下错误:“日历 System.Globalization.GregorianCalendar 不支持字符串表示的 DateTime。”

为了解决这个问题,我实现了一个 WCF 消息检查器,我想在“AfterReceiveReply”方法中修改消息。在这里,我想找到错误的日期并进行更正,然后再将其发送回客户。

我会通过字符串操作(查找+替换)来进行替换。

但是,我无法将回复消息的内容作为字符串检索。我尝试通过“reply.GetReaderAtBodyContents()”执行此操作,然后获取“OuterXml”,但这最终会因与上述相同的错误而崩溃,因为它将它作为 XML 对象读取(并且它包含无效的日期)。

异常详情

我也尝试了以下操作,但我也遇到了同样的问题(因为我们无法将无效的 XML 写入 XmlWrite 对象),它会引发与上述相同的错误。

MemoryStream ms = new MemoryStream();
XmlWriter xw = XmlWriter.Create(ms);
reply.WriteMessage(xw);
xw.Flush();
string body = Encoding.UTF8.GetString(ms.ToArray());
xw.Close();

Message 对象只接受“XmlWriter”或“XmlDictionaryWriter”来写入不同的流: 代码片段

有什么想法可以将回复消息的原始正文内容作为字符串对象获取,而无需将其解析为 XML 文档?

提前致谢!

此致

标签: c#wcf

解决方案


如果我们假设您从 GetReaderAtBodyContents 获得的 XmlDictionary 不会自行验证,那么这个忽略 FormatExceptions 的小心 XmlReader 可能足以让您的 dateTime 重写。

这是实现:

void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object state) 
{
    // settings to ignore errors ...
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ValidationType = ValidationType.Schema;
    settings.ValidationEventHandler += (o,e) => { 
        if (e.Exception != null && !(e.Exception.InnerException is FormatException))
        {
          throw e.Exception;
        }
    }; // ignore formatexception errors

    var xmlrdr = XmlReader.Create(reply.GetReaderAtBodyContents(),settings);
    var ms = new MemoryStream();
    using(var wr = XmlWriter.Create(ms))
    while(xmlrdr.Read())
    {
        // very simple and naive xml copy
         switch(xmlrdr.NodeType) 
         {
             case XmlNodeType.Element:
                 wr.WriteStartElement(xmlrdr.Name);
             break;
             case XmlNodeType.Text:
                 var text = xmlrdr.ReadContentAsString();
                 // names of nodes that are a date
                 if (xmlrdr.Name == "date") 
                 {
                     DateTime dtm;
                     if (!DateTime.TryParse(text, out dtm)) 
                     {
                         // let's assume this is the only thing that can go wrong
                         text = text.Replace("24:00:00", "23:59:59");
                     }
                     wr.WriteString(text);
                     wr.WriteEndElement(); // yeah, well, let's gamble
                 } 
                 else
                 {
                     wr.WriteString(text);
                 }
             break;
             case XmlNodeType.EndElement:
                 wr.WriteEndElement();
             break;
             default:
               // Debug.Assert
             break;
         }
    }
    ms.Position=0;
    // create a new message for the next handler
    reply = System.ServiceModel.Channels.Message.CreateMessage(
       reply.Version, 
       reply.Headers.Action, 
       XmlReader.Create(ms));

}

推荐阅读