jjzjj

c# - WCF 客户端 : Forcing Global Namespaces

coder 2024-05-21 原文

我正在处理与 SOAP 服务的接口(interface),该服务似乎不处理默认 namespace ,但可以很好地处理在 SOAP 信封级别声明的全局 namespace 和 namespace 前缀。

问题是 WCF 不会在根目录下创建这些全局命名空间,而是使用显式的无前缀默认命名空间,而服务显然会阻塞这些默认命名空间。现在我知道这并不是 WCF 的错——我相信 WCF 生成的消息是有效的 XML,但服务仍然会阻塞它。

使用 WCF 生成的输出如下所示:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:Security xmlns:h="http://docs.oasis-open.org/wss/2004/01/oasis-
          ...
    </h:Security>
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <cancelShipmentRequest xmlns="http://www.royalmailgroup.com/api/ship/V2">
      <integrationHeader>
        <dateTime xmlns="http://www.royalmailgroup.com/integration/core/V1">2016-03-26T01:44:37.0493801Z</dateTime>
        <version xmlns="http://www.royalmailgroup.com/integration/core/V1">2</version>
        <identification xmlns="http://www.royalmailgroup.com/integration/core/V1">
          <applicationId>RMG-API-G-01</applicationId>
          <transactionId>ozhckwej6sxg</transactionId>
        </identification>
      </integrationHeader>
      <cancelShipments>
        <shipmentNumber>TTT001908905GB</shipmentNumber>
      </cancelShipments>
    </cancelShipmentRequest>
  </s:Body>
</s:Envelope>

这是行不通的。

使用以下 SOAP 信封(在 SoapUI 中手动)确实有效:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:oas="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
  xmlns:v2="http://www.royalmailgroup.com/api/ship/V2"
  xmlns:v1="http://www.royalmailgroup.com/integration/core/V1">
  <soapenv:Header>
    <h:Security xmlns:h="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       ...
    </h:Security>
  </soapenv:Header>
  <soapenv:Body>
    <v2:cancelShipmentRequest>
      <v2:integrationHeader>
        <v1:dateTime>2016-03-02T14:55:00Z</v1:dateTime>
        <v1:version>2</v1:version>
        <v1:identification>
          <v1:applicationId>RMG-API-G-01</v1:applicationId>
          <v1:transactionId>wftdaife96gv</v1:transactionId>
        </v1:identification>
      </v2:integrationHeader>
      <v2:cancelShipments>
        <v2:shipmentNumber>TTT001908905GB</v2:shipmentNumber>
      </v2:cancelShipments>
    </v2:cancelShipmentRequest>
  </soapenv:Body>
</soapenv:Envelope>

两者的区别在于 v1 和 v2 命名空间是在文档顶部全局声明的,而第二个文档中没有局部命名空间声明。

也许我遗漏了什么,但对我来说,WCF 生成的 XML 看起来是有效的,并且在命名空间方面代表相同的文档状态。

我能说的唯一区别是命名空间的声明方式。尽管 WCF 版本似乎有效并生成相同的命名空间,但服务会提示命名空间引用无效。

Failed Schema Validation: Message failed schema validation: Schemas validity error : Element 'xmlns': This element is not expected. Expected is ( {http://www.royalmailgroup.com/api/ship/V2}integrationHeader ).

问题是,强制 WCF 在顶部而不是内联添加命名空间引用的最佳方法是什么?到目前为止,我发现的唯一方法是使用消息检查器并显式重写消息,但如果我完成所有这些,我还不如手动创建消息。

我有什么想法可以尝试强制 WCF 使用显式 namespace 前缀而无需手动重写消息?

最佳答案

所以这个问题的答案是创建一个自定义的 IClientMessageFormatterMessage 然后覆盖 Message.OnWriteStartEnvelope() 以显式写出Soap 文档根目录下的所有命名空间。呈现的文档然后重用这些命名空间,而不是在子元素上显式分配命名空间。

为此需要创建 3 个类:

  • 处理实际 OnWriteStartEnvelope()
  • 的消息实现
  • 挂接到 WCF 的 IClientMessageFormatter
  • FormatMessageAttribute 附加到客户端的每个方法

这是所有三个的代码:

public class RoyalMailCustomMessage : Message
{
    private readonly Message message;

    public RoyalMailCustomMessage(Message message)
    {
        this.message = message;
    }
    public override MessageHeaders Headers
    {
        get { return this.message.Headers; }
    }
    public override MessageProperties Properties
    {
        get { return this.message.Properties; }
    }
    public override MessageVersion Version
    {
        get { return this.message.Version; }
    }

    protected override void OnWriteStartBody(XmlDictionaryWriter writer)
    {
        writer.WriteStartElement("Body", "http://schemas.xmlsoap.org/soap/envelope/");
    }
    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        this.message.WriteBodyContents(writer);
    }
    protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer)
    {
        writer.WriteStartElement("soapenv", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/");
        writer.WriteAttributeString("xmlns", "oas", null, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        writer.WriteAttributeString("xmlns", "v2", null, "http://www.royalmailgroup.com/api/ship/V2");
        writer.WriteAttributeString("xmlns", "v1", null, "http://www.royalmailgroup.com/integration/core/V1");
        writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
        writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");            
    }
}

public class RoyalMailMessageFormatter : IClientMessageFormatter
{
    private readonly IClientMessageFormatter formatter;

    public RoyalMailMessageFormatter(IClientMessageFormatter formatter)
    {
        this.formatter = formatter;
    }

    public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
    {
        var message = this.formatter.SerializeRequest(messageVersion, parameters);
        return new RoyalMailCustomMessage(message);
    }

    public object DeserializeReply(Message message, object[] parameters)
    {
        return this.formatter.DeserializeReply(message, parameters);
    }
}


[AttributeUsage(AttributeTargets.Method)]
public class RoyalMailFormatMessageAttribute : Attribute, IOperationBehavior
{
    public void AddBindingParameters(OperationDescription operationDescription,
        BindingParameterCollection bindingParameters)
    { }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {
        var serializerBehavior = operationDescription.Behaviors.Find<XmlSerializerOperationBehavior>();

        if (clientOperation.Formatter == null)
            ((IOperationBehavior)serializerBehavior).ApplyClientBehavior(operationDescription, clientOperation);

        IClientMessageFormatter innerClientFormatter = clientOperation.Formatter;
        clientOperation.Formatter = new RoyalMailMessageFormatter(innerClientFormatter);
    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    { }

    public void Validate(OperationDescription operationDescription) { }
}

其中大部分是仪式和样板代码。关键代码片段是连接实际命名空间的 OnWriteStartEnvelope、格式化程序连接到 WCF 管道的 SerializeRequest 和消息的 ApplyClientBehavior formatter是附在实际操作上的。

为了连接起来,我将属性添加到服务接口(interface)上的客户端方法 - 在本例中是在 Reference.cs 中生成的 WCF 客户端中。

    // CODEGEN: Generating message contract since the operation cancelShipment is neither RPC nor document wrapped.
    [System.ServiceModel.OperationContractAttribute(Action="cancelShipment", ReplyAction="*")]
    [System.ServiceModel.FaultContractAttribute(typeof(MarvelPress.Workflow.Business.RoyalShippingApi.exceptionDetails), Action="cancelShipment", Name="exceptionDetails")]
    [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(contactMechanism))]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(baseRequest))]
    [RoyalMailFormatMessage()]
    MarvelPress.Workflow.Business.RoyalShippingApi.cancelShipmentResponse1 cancelShipment(MarvelPress.Workflow.Business.RoyalShippingApi.cancelShipmentRequest1 request);

从 WCF 生成的消息现在看起来与预期的一样,所有命名空间都在文档顶部定义:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:oas="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:v2="http://www.royalmailgroup.com/api/ship/V2" xmlns:v1="http://www.royalmailgroup.com/integration/core/V1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <h:Security>...</h:Security>
  </s:Header>
  <soapenv:Body>
    <v2:cancelShipmentRequest>
      <v2:integrationHeader>
        <v1:dateTime>2016-04-02T01:04:50.4122473Z</v1:dateTime>
        <v1:version>2</v1:version>
        <v1:identification>
          <v1:applicationId>RMG-API-G-01</v1:applicationId>
          <v1:transactionId>fshrxevdnc7n</v1:transactionId>
        </v1:identification>
      </v2:integrationHeader>
      <v2:cancelShipments>
        <v2:shipmentNumber>TTT001908905GB</v2:shipmentNumber>
      </v2:cancelShipments>
    </v2:cancelShipmentRequest>
  </soapenv:Body>
</soapenv:Envelope>    

有关更多信息和添加格式化程序的通用命名空间,请在此处查看我的相关博客文章:http://weblog.west-wind.com/posts/2016/Apr/02/Custom-Message-Formatting-in-WCF-to-add-all-Namespaces-to-the-SOAP-Envelope

关于c# - WCF 客户端 : Forcing Global Namespaces,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36230835/

有关c# - WCF 客户端 : Forcing Global Namespaces的更多相关文章

  1. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  2. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  3. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  4. ruby - 在 TCPServer (Ruby) 中,我如何从客户端获取 IP/MAC? - 2

    我想在Ruby的TCPServer中获取客户端的IP地址。以及(如果可能的话)MAC地址。例如,Ruby中的时间服务器,请参阅评论。tcpserver=TCPServer.new("",80)iftcpserverputs"Listening"loopdosocket=tcpserver.acceptifsocketThread.newdoputs"Connectedfrom"+#HERE!HowcanigettheIPAddressfromtheclient?socket.write(Time.now.to_s)socket.closeendendendend非常感谢!

  5. c# - C# 中的 Flatten Ruby 方法 - 2

    我如何做Ruby方法"Flatten"RubyMethod在C#中。此方法将锯齿状数组展平为一维数组。例如:s=[1,2,3]#=>[1,2,3]t=[4,5,6,[7,8]]#=>[4,5,6,[7,8]]a=[s,t,9,10]#=>[[1,2,3],[4,5,6,[7,8]],9,10]a.flatten#=>[1,2,3,4,5,6,7,8,9,10 最佳答案 递归解决方案:IEnumerableFlatten(IEnumerablearray){foreach(variteminarray){if(itemisIEnume

  6. ruby - 可以像在 C# 中使用#region 一样在 Ruby 中使用 begin/end 吗? - 2

    我最近从C#转向了Ruby,我发现自己无法制作可折叠的标记代码区域。我只是想到做这种事情应该没问题:classExamplebegin#agroupofmethodsdefmethod1..enddefmethod2..endenddefmethod3..endend...但是这样做真的可以吗?method1和method2最终与method3是同一种东西吗?还是有一些我还没有见过的用于执行此操作的Ruby惯用语? 最佳答案 正如其他人所说,这不会改变方法定义。但是,如果要标记方法组,为什么不使用Ruby语义来标记它们呢?您可以使用

  7. c# - Ruby 等效于 C# Linq 聚合方法 - 2

    什么是Linq聚合方法的ruby​​等价物。它的工作原理是这样的varfactorial=new[]{1,2,3,4,5}.Aggregate((acc,i)=>acc*i);每次将数组序列中的值传递给lambda时,变量acc都会累积。 最佳答案 这在数学以及几乎所有编程语言中通常称为折叠。它是更普遍的变形概念的一个实例。Ruby从Smalltalk中继承了这个特性的名称,它被称为inject:into:(像aCollectioninject:aStartValueinto:aBlock一样使用。)所以,在Ruby中,它称为inj

  8. c# - 先学什么? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭8年前。Improvethisquestion几年前我去学校学习编程,毕业后我找到了一份系统管理方面的工作,这就是我职业生涯的方向。我想重新开始某种开发,并且一直在“玩”C#和ASP.NET,但我已经听到很多关于其他"new"语言的讨论(新的意思是它们是新的)我)喜欢Ruby和F#。我想我想知道我是否在浪费时间学习主要的MS语言,而不是成为一名通才。很长一段时间没有离开开发社区(如果我曾经离开过的话)让我在潮流中挣扎,我不想落在时代的

  9. c# - 在 C# 中重现 Ruby OpenSSL private_encrypt 输出 - 2

    我有一个简单的Ruby脚本,我用它在某些HTTPheader上执行private_encrypt以签署要发送到ruby​​RESTAPI的Web请求,该API会根据Base64编码字符串测试Base64编码字符串生成而不是解码Base64和解密数据然后测试原始字符串。我使用的脚本是require"openssl"require"base64"path_to_cert=ARGV[0].dupplain_text=Base64.decode64(ARGV[1].dup)private_key=OpenSSL::PKey::RSA.new(File.read(path_to_cert))pu

  10. ruby-on-rails - 为什么我必须在使用客户验证器后重新加载 rspec 中的记录? - 2

    我有一个模型User,它在创建后的回调中创建了选项#Userhas_one:user_optionsafter_create:create_optionsprivatedefcreate_optionsUserOptions.create(user:self)end我对此有一些简单的Rspec覆盖:describe"newuser"doit"createsuser_optionsaftertheuseriscreated"douser=create(:user)user.user_options.shouldbe_kind_of(UserOptions)endend一切正常,直到我将自

随机推荐