C # WCF Global Namespaces - Royal Mail

I have a SOAP WCF client that generates a request. This is rejected by the server as an invalid request. I traced it to namespaces using SOAPUI, but I can’t figure out how I can get the client to execute the desired result.

The client was generated as a web service link from wsdl and generates 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, namespaces are displayed on separate elements ...

The working example that 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 matter, but the server simply rejects the request. After modifying my generated request in SOAPUI, it causes a problem, so how can I move the two namespace definitions v1 and v2 to SOAP Envelope, and then is it correct to use the correct prefix?

My client starts 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; } 
+5
source share
1 answer

So it turned out that I needed to create my own MessageFormatter and attach it as a behavior to client operations.

For someone who needs this, you need 3 files;

First, you create a custom message class that 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 that implements IClientMessageFormatter. This uses the Message class, which 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); } } 

Then we need to create a custom class that implements IOperationBehavior. This is necessary so that we can adapt the custom message formatter to service actions as behavior;

 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 behavior to all WCF generated service operations;

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

Now the namespaces should be in the SOAP envelope, and all elements use the correct prefix, which gives 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> 
+5
source

All Articles