WCF: Simple way to modify serialized response

Problem description

This type of problem generally appears in interop scenarios where client could be .net application and service could be java web service.

  1. .net client application is able to send request.
  2. Java service is able to read the request.
  3. Java service is able to send the response.
  4. However, .net client application is not able to read the response. Client application is just able to read the content as null. Interestingly in fiddler (a web traffic tool) or in netmon (a network monitor tool), we can see actual SOAP payload in response. Question appears why .net is not able to read the payload.

 

 

Issue

It primarily happens due to namespace conflict in xsd-complex-type-elements. In other words, message could not be properly de-serialized to the structure of .net classes. As a best practice, wsdl can be modified. In some cases, .net applications do not have control over web service side (as in vendor).

Solution

From WCF traces as well as SOAP UI tool, we identified the response appears as:

 

 

  • Open response editor for the method in SOAP UI
  • Paste the same content (from WCF traces or use SOAP UI response)
  • Right click on the editor
  • Select Validate
  • Validation is unsuccessful

Note

  • In my case, validation pointed that it has issues with AccountResult node.
  • Applied ns2:AccountResult in place of AccountResult. Validation was successful.

It is safe to claim on payload modification, response can properly be de-serialized in the .net side.

How?

We need to apply code changes in the .net side.

  1. Create a new class CustomInspector (implement IClientMessageInspector) and modify AfterReceiveReply method to correct the anomaly.
  using System.ServiceModel.Dispatcher; 
 using System.Xml; 
 using System.IO; 
 namespace MyApplication.net 
 { 
 public class CustomInspector: IClientMessageInspector 
 { 
 public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) 
 { 
 // Read reply payload 
 XmlDocument doc = new XmlDocument(); 
 MemoryStream ms = new MemoryStream(); 
 XmlWriter writer = XmlWriter.Create(ms); 
 reply.WriteMessage(writer); 
 writer.Flush(); 
 ms.Position = 0; 
 doc.Load(ms); 
 
 // Change logic 
 ChangeMessage(doc); 
 
 // Write the reply payload 
 ms.SetLength(0); 
 writer = XmlWriter.Create(ms); 
 doc.WriteTo(writer); 
 writer.Flush(); 
 ms.Position = 0; 
 XmlReader reader = XmlReader.Create(ms); 
 reply = System.ServiceModel.Channels.Message.CreateMessage(reader, int.MaxValue, reply.Version); 
 } 
 
 void ChangeMessage(XmlDocument doc) 
 { 
 string xml = doc.OuterXml;  
  // Apply the xml modification logic here 
 // Replace can be applied as a pure string replacement for string types 
 xml = xml.Replace("AccountResult", "ns2:AccountResult"); 
 doc.LoadXml(xml); 
 } 
 
 public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) 
 { 
 return null; 
 }
 } 
 } 
 

 2.       Create a custom class CustomValidationBehavior (implement IEndpointBehavior) and modify logic for ApplyClientBehavior method.

  using System.Text; 
 using System.ServiceModel.Description;
  namespace MyApplication.net 
 { 
 public class CustomValidationBehavior: IEndpointBehavior 
 { 
 public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
 { 
 }
  public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) 
 { 
 clientRuntime.MessageInspectors.Add(new CustomInspector()); 
 } 
 public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) 
 {
  } 
 public void Validate(ServiceEndpoint endpoint) 
 {
  } 
 } 
  } 

 3.       Now, we need to associate this custom endpoint behavior with the service proxy instance as in:

  AccountServiceReference.Service1Client proxy = new AccountServiceReference.Service1Client();
 proxy.Endpoint.Behaviors.Add(new CustomValidationBehavior()); 
 // Make service method calls 
 // Get results  

4.       Client should be able to read the response payload successfully for all nodes. If it has any issues with a particular xml node, we need to identify if any element does not fit in with wsdl defined SOAP namespace.

5.       If the similar situation applies in case of a WCF service where client is java, service can take advantage of custom IDispatchMessageInspector in reverse to IClientMessageinspector.

 

Note: There are certain code formatting challenges in the shared codes above. Please consider the connected blocks of code as a single unit. Keep coding! 

Hope this helps!

 

Created by

Purna Chandra Panda (MSFT)