简体   繁体   English

C#WCF全球命名空间 - 皇家邮政

[英]C# WCF Global Namespaces - Royal Mail

I've got a WCF SOAP client which is generating a request. 我有一个生成请求的WCF SOAP客户端。 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. 我已经使用SOAPUI将其追溯到命名空间,但无法弄清楚如何让客户端生成所需的结果。

The client was generated as a web service reference from the wsdl and is producing the following SOAP message; 客户端是从wsdl生成的Web服务引用,并生成以下SOAP消息;

<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; 我的工作示例在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? 在SOAPUI中修改生成的请求之后,它肯定会导致问题,那么如何将两个命名空间定义v1和v2移动到SOAP信封中,然后让正确的元素使用正确的前缀?

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. 所以事实证明我需要创建一个自定义MessageFormatter并将其作为行为附加到客户端操作。

For anybody else needing to do this you need 3 files; 对于其他需要这样做的人,你需要3个文件;

Firstly you create a custom message class which implements Message. 首先,您创建一个实现Message的自定义消息类。 Here in the OnWriteStartEnvelope method you add/define the namespaces you want in the Envelope. 在OnWriteStartEnvelope方法中,您可以在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. 然后创建一个实现IClientMessageFormatter的自定义类。 This makes use of the Message class we defined above for outgoing requests made by the client; 这使得我们在上面定义的Message类用于客户端发出的传出请求;

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. 然后,我们需要创建一个实现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; 最后,我们需要将自定义IOperation行为添加到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; 命名空间现在应该在SOAP Envelope中,并且元素都使用正确的前缀给我们类似的东西;

<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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM