C#WCF命名空间移动到页眉和放大器;使用NS前缀 [英] C# WCF Namespaces Move To Header & Use NS Prefix

查看:193
本文介绍了C#WCF命名空间移动到页眉和放大器;使用NS前缀的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个WCF SOAP客户端是产生一个请求。这是正在由服务器作为一个无效的请求被拒绝。我已经追查到使用SOAPUI命名空间,但无法弄清楚怎样才能得到客户端产生所需的结果。



客户端被作为Web服务引用生成从WSDL和正在产生以下SOAP消息;

 < S:信封的xmlns:S =HTTP://模式.xmlsoap.org / SOAP /信封/> 
< S:头>< / S:页眉和GT;
< S:身体的xmlns:XSI =htt​​p://www.w3.org/2001/XMLSchema-instance的xmlns:XSD =htt​​p://www.w3.org/2001/XMLSchema> ;
< createShipmentRequest的xmlns =htt​​p://www.royalmailgroup.com/api/ship/V2>
< integrationHeader>
<日期时间的xmlns =htt​​p://www.royalmailgroup.com/integration/core/V1>学习网站< /日期>
<版本的xmlns =htt​​p://www.royalmailgroup.com/integration/core/V1> 2'; /版本>
<鉴定的xmlns =htt​​p://www.royalmailgroup.com/integration/core/V1>
<&的applicationID GT;有的随机数LT; /&的applicationID GT;
< transactionId,有无>有一定的参考号和LT; / transactionId,有无>
< /识别>
< / integrationHeader>
< / createShipmentRequest>
< / S:身体与GT;
< / S:信封>



正如你所看到的命名空间正在对单个元件输出...



工作的例子,我有有在SOAP信封定义的名字空间;

 <小号:信封的xmlns:V2 =htt​​p://www.royalmailgroup.com/api/ship/V2的xmlns:V1 =htt​​p://www.royalmailgroup.com/integration/core/V1的xmlns:S = http://schemas.xmlsoap.org/soap/envelope/的xmlns:U =http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0。 XSD> 
< S:头>< / S:页眉和GT;
< S:身体与GT;
< V2:createShipmentRequest>
< V2:integrationHeader>
< V1:日期时间>学习网站< / V1:日期时间>
< V1:版本> 2'; / V1:版本>
< V1:识别>
< V1:&的applicationID GT;有的随机数LT; / V1:&的applicationID GT;
< V1:transactionId,有无>有一定的参考号和LT; / V1:transactionId,有无>
< / V1:识别>
< / V2:integrationHeader>
< / V2:createShipmentRequest>
< / S:身体与GT;
< / S:信封>



据我了解这不应该有所作为,但断绝只是拒绝该请求。修改我产生请求SOAPUI是这个挑衅引起的问题后,让我怎么能移动两个命名空间定义V1和放大器; V2的SOAP信封,然后有正确的元素使用正确的前缀



我的客户端使用下面的函数开始;

 私人shippingAPIPortTypeClient GetProxy(){

basicHttpBinding的myBinding =新basicHttpBinding的(BasicHttpSecurityMode.Transport);
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

shippingClient =新shippingAPIPortTypeClient(myBinding,新的EndpointAddress(新的URI(shippingClientSandboxEndpoint),EndpointIdentity.CreateDnsIdentity(api.royalmail.com),新AddressHeaderCollection()));
shippingClient.ClientCredentials.ClientCertificate.Certificate =证书;

返回shippingClient;
}


解决方案

因此​​,原来我需要要创建一个自定义MessageFormatter并附加它作为一个行为的客户端操作



有关其他人需要做到这一点,你需要3个文件;



首先,你创建一个实现消息的自定义邮件类。这里在OnWriteStartEnvelope方法添加/定义您在信封想要的命名空间。



 类RoyalMailMessage:消息{
私人只读邮件消息;

公共RoyalMailMessage(消息消息){
this.message =消息;
}
公众覆盖MessageHeaders头{
获得{
返回this.message.Headers;
}
}
公众覆盖MessageProperties属性{
获得{
返回this.message.Properties;
}
}
公众覆盖MessageVersion版本{
获得{
返回this.message.Version;
}
}
保护覆盖无效OnWriteStartBody(作家的XmlDictionaryWriter){
writer.WriteStartElement(身体,http://schemas.xmlsoap.org/soap/envelope/ );
}
保护覆盖无效OnWriteBodyContents(作家的XmlDictionaryWriter){
this.message.WriteBodyContents(作家);
}
保护覆盖无效OnWriteStartEnvelope(作家的XmlDictionaryWriter){
writer.WriteStartElement(S,信封,http://schemas.xmlsoap.org/soap/envelope/ );
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);

}
}



然后创建一个自定义类,实现IClientMessageFormatter。这使得利用我们以上由客户端发出的请求定义Message类的;

 公共类RoyalMailMessageFormatter:IClientMessageFormatter {
私人只读IClientMessageFormatter格式;

公共RoyalMailMessageFormatter(IClientMessageFormatter格式化){
this.formatter =格式;
}

公共对象DeserializeReply(消息消息,对象[]参数){
返回this.formatter.DeserializeReply(消息,参数);
}

公共信息SerializeRequest(MessageVersion messageVersion,对象[]参数){
VAR消息= this.formatter.SerializeRequest(messageVersion,参数);
返回新RoyalMailMessage(消息);
}
}



然后,我们需要创建一个实现IOperationBehavior的自定义类。这是必要的,所以我们可以attatch的自定义消息格式化为一个问题的服务业务;

 类RoyalMailIEndpointBehavior:IOperationBehavior {

公共RoyalMailIEndpointBehavior(){}

公共无效ApplyClientBehavior(OperationDescription描述,ClientOperation代理){
IClientMessageFormatter currentFormatter = proxy.Formatter;
proxy.Formatter =新RoyalMailMessageFormatter(currentFormatter);
}

公共无效AddBindingParameters(OperationDescription operationDescription,BindingParameterCollection bindingParameters){

}

公共无效ApplyDispatchBehavior(OperationDescription operationDescription,DispatchOperation dispatchOperation ){

}

公共无效验证(OperationDescription operationDescription){

}

}

最后,我们需要定制一个IOperation行为添加到WCF所产生的所有业务运营;

 私人shippingAPIPortTypeClient GetProxy(){

basicHttpBinding的myBinding =新basicHttpBinding的(BasicHttpSecurityMode.Transport);
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

shippingClient =新shippingAPIPortTypeClient(myBinding,新的EndpointAddress(新的URI(shippingClientSandboxEndpoint),EndpointIdentity.CreateDnsIdentity(api.royalmail.com),新AddressHeaderCollection()));
shippingClient.ClientCredentials.ClientCertificate.Certificate =证书;

的foreach(在shippingClient.Endpoint.Contract.Operations OperationDescription OD){
od.Behaviors.Add(新RoyalMailIEndpointBehavior());
}
返回shippingClient;
}



该命名空间现在应该在SOAP信封和元素都使用正确的前缀给我们的东西等;

 < S:信封的xmlns:S =htt​​p://schemas.xmlsoap.org/ SOAP /信封/的xmlns:V2 =http://www.royalmailgroup.com/api/ship/V2的xmlns:V1 =http://www.royalmailgroup.com/integration/core/V1的xmlns:XSI =htt​​p://www.w3.org/2001/XMLSchema-instance的xmlns:XSD =htt​​p://www.w3.org/2001/XMLSchema> 
< S:头>< / S:页眉和GT;
< S:身体与GT;
< V2:createShipmentRequest>
< V2:integrationHeader>
< V1:日期时间> 2015-07-23T20:37:07.937 + 01:00< / V1:日期时间>
< V1:版本> 2'; / V1:版本>
< V1:识别>
< V1:&的applicationID GT;一些随机ID< / V1:&的applicationID GT;
< V1:transactionId,有无>一些随机ID< / V1:transactionId,有无>
< / V1:识别>
< / V2:integrationHeader>
< / V2:createShipmentRequest>
< / S:身体与GT;
< / S:信封>


I've got a WCF SOAP client which is generating a request. This is being refused by the server as an invalid request. I've traced it down to namespaces using SOAPUI but cannot figure out how I can get the client to produce the required result.

The client was generated as a web service reference from the wsdl and is producing the following SOAP message;

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header></s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <createShipmentRequest xmlns="http://www.royalmailgroup.com/api/ship/V2">
      <integrationHeader>
        <dateTime xmlns="http://www.royalmailgroup.com/integration/core/V1">2015-07-23</dateTime>
        <version xmlns="http://www.royalmailgroup.com/integration/core/V1">2</version>
        <identification xmlns="http://www.royalmailgroup.com/integration/core/V1">
          <applicationId>some random number</applicationId>
          <transactionId>some reference number</transactionId>
        </identification>
      </integrationHeader>
    </createShipmentRequest>
  </s:Body>
</s:Envelope>

As you can see the namespaces are being outputted on the individual elements...

The working example I have has the namespaces defined in the SOAP Envelope;

<s:Envelope xmlns:v2="http://www.royalmailgroup.com/api/ship/V2" xmlns:v1="http://www.royalmailgroup.com/integration/core/V1" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <s:Header></s:Header>
  <s:Body>
    <v2:createShipmentRequest>
      <v2:integrationHeader>
        <v1:dateTime>2015-07-23</v1:dateTime>
        <v1:version>2</v1:version>
        <v1:identification>
          <v1:applicationId>some random number</v1:applicationId>
          <v1:transactionId>some reference number</v1:transactionId>
        </v1:identification>
      </v2:integrationHeader>
    </v2:createShipmentRequest>
  </s:Body>
</s:Envelope>

From what I understand this should not make a difference but the sever simply rejects the request. After modifying my generated request in SOAPUI it is defiantly this causing the problem, so how can I move the two namespace definitions v1 & v2 to the SOAP Envelope and then have the correct elements use the correct prefix?

My client is initiated using the following function;

private shippingAPIPortTypeClient GetProxy() {

  BasicHttpBinding myBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
  myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

  shippingClient = new shippingAPIPortTypeClient(myBinding, new EndpointAddress(new Uri(shippingClientSandboxEndpoint), EndpointIdentity.CreateDnsIdentity("api.royalmail.com"), new AddressHeaderCollection()));
  shippingClient.ClientCredentials.ClientCertificate.Certificate = certificate;

  return shippingClient;
}

解决方案

So it turns out I needed to create a custom MessageFormatter and attach it as a behaviour to the client operations.

For anybody else needing to do this you need 3 files;

Firstly you create a custom message class which implements Message. Here in the OnWriteStartEnvelope method you add/define the namespaces you want in the Envelope.

class RoyalMailMessage: Message {
  private readonly Message message;

  public RoyalMailMessage(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("s", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/");
    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");

  }
}

Then you create a custom Class which implements IClientMessageFormatter. This makes use of the Message class we defined above for outgoing requests made by the client;

public class RoyalMailMessageFormatter: IClientMessageFormatter {
  private readonly IClientMessageFormatter formatter;

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

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

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

We then need to create a custom class which implements IOperationBehavior. This is needed so we can attatch the custom message formatter to the service operations as a behaviour;

class RoyalMailIEndpointBehavior: IOperationBehavior {

  public RoyalMailIEndpointBehavior() {}

  public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy) {
    IClientMessageFormatter currentFormatter = proxy.Formatter;
    proxy.Formatter = new RoyalMailMessageFormatter(currentFormatter);
  }

  public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) {

  }

  public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) {

  }

  public void Validate(OperationDescription operationDescription) {

  }

}

Finally, we need to add the custom IOperation behaviour to all the service operations generated by WCF;

private shippingAPIPortTypeClient GetProxy() {

  BasicHttpBinding myBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
  myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;

  shippingClient = new shippingAPIPortTypeClient(myBinding, new EndpointAddress(new Uri(shippingClientSandboxEndpoint), EndpointIdentity.CreateDnsIdentity("api.royalmail.com"), new AddressHeaderCollection()));
  shippingClient.ClientCredentials.ClientCertificate.Certificate = certificate;

  foreach(OperationDescription od in shippingClient.Endpoint.Contract.Operations) {
    od.Behaviors.Add(new RoyalMailIEndpointBehavior());
  }
  return shippingClient;
}

The namespaces should now be in the SOAP Envelope and the elements all use the correct prefix giving us something like;

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" 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></s:Header>
  <s:Body>
    <v2:createShipmentRequest>
      <v2:integrationHeader>
        <v1:dateTime>2015-07-23T20:37:07.937+01:00</v1:dateTime>
        <v1:version>2</v1:version>
        <v1:identification>
          <v1:applicationId>SOME RANDOM ID</v1:applicationId>
          <v1:transactionId>SOME RANDOM ID</v1:transactionId>
        </v1:identification>
      </v2:integrationHeader>
    </v2:createShipmentRequest>
  </s:Body>
</s:Envelope>

这篇关于C#WCF命名空间移动到页眉和放大器;使用NS前缀的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆