c# - 使用 Web 服务客户端/ClientBase 检测无效的 XML 响应

标签 c# .net xml

我们目前正在使用 Web 服务 (IBM Message Broker)。由于该服务仍在开发中,在许多情况下它会返回无效的 XML(是的,我保证会修复这个问题)。

从 .NET 调用此服务时出现问题,使用由 svcutil 使用 ClientBase<T> 生成的客户端.似乎XmlSerializer used 不会因无效的 XML 元素而出错。

下面是一个没有报错,只返回一个部分初始化的元素的例子:

using System;
using System.Diagnostics;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

[Serializable]
public class Program
{
  [XmlElement(Order = 0)]
  public string One { get;set; }

  [XmlElement(Order = 1)]
  public string Two { get;set; }

  static void Main(string[] args)
  {
    var ser = new XmlSerializer(typeof(Program));
    ser.UnknownElement += (o, e) => { 
      Console.WriteLine("Unknown element: {0}", e.Element.Name); 
    };

    using (var input = new StringReader(
@"<?xml version=""1.0"" encoding=""utf-8"" ?>
<Program>
  <Two>Two</Two>
  <One>One</One>
</Program>"))
    {
      var p = (Program)ser.Deserialize(input);
      Debug.Assert(p.One != null);
    }
  }
}

附加到 UnknownElement 时事件,它正确地报告了无效的 XML(元素顺序不匹配),但是当使用 ClientBase<T> 时,这些(和其他一些情况)被简单地忽略(就好像没有使用 XmlSerializer 的故障事件)。

我的问题是如何制作 ClientBase<T>检测无效的 XML?有没有办法 Hook XmlSerializer 的故障事件?被 ClientBase<T> 使用?

目前,如果某些内容不合理,我们必须使用 SoapUI 手动检查响应。

谢谢

最佳答案

因此,开箱即用的 WCF 不相信 XML 验证。它将 XML 视为一种消息格式,读取看起来正确的信息并忽略其余信息。这样做的好处是服务接受的内容非常自由。

当诸如元素顺序之类的事情开始变得重要时,麻烦就来了。可能有人认为结构的排序不应该很重要,您可以使用数据本身中的信息(例如日期、时间或索引属性)来指示排序。在你的小案例中,顺序实际上并不重要,因为你可以阅读和理解信息,而不管它的呈现顺序如何。我相信你的实际案例更有效,所以我赢了'进一步研究这一点。

为了验证 XML 结构,您需要访问 WCF 管道中的消息。最简单的方法是使用 IClientMessageInspector验证消息并使用行为将其附加到您的客户端的实现。

假设您想要通过针对 XSD 的 XML 架构验证来执行此操作,您将创建一个如下所示的检查器:

class XsdValidationInspector : IClientMessageInspector
{
    private readonly XmlSchemaSet _schemas;

    public XsdValidationInspector(XmlSchemaSet schemas)
    {
        this._schemas = schemas;
    }

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        // Buffer the message so we can read multiple times.
        var buffer = reply.CreateBufferedCopy();

        // Validate the message content.
        var message = buffer.CreateMessage();

        using (var bodyReader
            = message.GetReaderAtBodyContents().ReadSubTree())
        {
            var settings = new XmlReaderSettings
            {
                Schemas = this._schemas,
                ValidationType = ValidationType.Schema,
            };

            var events = new List<ValidationEventArgs>();
            settings.ValidationEventHandler += (sender, e) => events.Add(e);

            using (var validatingReader
                = XmlReader.Create(bodyReader, settings))
            {
                // Read to the end of the body.
                while(validatingReader.Read()) {  }
            }

            if (events.Any())
            {
                // TODO: Examine events and decide whether to throw exception.
            }
        }

        // Assign a copy to be passed to the next component.
        reply = buffer.CreateMessage();
    }

    public object BeforeSendRequest(
        ref Message request,
        IClientChannel channel) {}
}

伴随的验证行为并不是特别复杂:

class XsdValiationBehavior : IEndpointBehavior
{
    private readonly XmlSchemaSet _schemas;

    public XsdValidationBehavior(XmlSchemaSet schemas)
    {
        this._schemas = schemas;
    }

    public void AddBindingParameters(
        ServiceEndpoint endpoint,
        BindingParameterCollection bindingParameters) {}

    public void ApplyClientBehavior(
        ServiceEndpoint endpoint,
        ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(
            new XsdValidationInspector(this._schemas));
    }

    public void ApplyDispatchBehavior(
        ServiceEndpoint endpoint,
        EndpointDispatcher endpointDispatcher) {}

    public void Validate(ServiceEndpoint endpoint){}
}

您可以创建一些配置元素并通过配置应用行为,或者您可以在打开客户端连接之前通过修改客户端的 channel 工厂以编程方式执行此操作。这是编程方法:

var schemaMarkup =  @"<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
       <xsd:element name='Program'>
        <xsd:complexType>
         <xsd:sequence>
          <xsd:element name='One' minOccurs='1' maxOccurs='1'/>
          <xsd:element name='Two' minOccurs='1' maxOccurs='1'/>
         </xsd:sequence>
        </xsd:complexType>
       </xsd:element>
      </xsd:schema>";

var schema = new XmlSchema();
using (var stringReader = new StringReader(schemaMarkup));
{
    var events = new List<ValidationEventArgs>();
    schema.Read(stringReader, (sender, e) => events.Add(e));

    // TODO: Check events for any errors.
}

var validation = new XsdValidationBehavior(new XmlSchemaSet { schema });

client.ChannelFactory.Behaviours.Add(validation);

关于c# - 使用 Web 服务客户端/ClientBase 检测无效的 XML 响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24882399/

相关文章:

c# - 如何统一创建一个基本的 ListView

c# - 如何在 ASP.NET Webform 中显示 HTML 文件

c# - vscode 有 csproj 生成器吗?

.net - 循环引用和 WCF

c# - 在 C# 中保存每个用户选项的最佳方式

c# - Linq 和维数组中的 SelectMany?

.net - 使用对象的字段作为通用字典键

xml - XML 解析器/验证器的算法复杂性

php - 如何在 Flex 中创建和访问带有属性的 xml

java - JOOX 命名空间支持