0

I am trying to develop a simple Windows Forms app to request tracking information from the latest version ESTES ShipmentTracking v1.1 web service. When I execute the Request, my program throws a System.ServiceModel.CommunicationException

I am using Visual Studio 2019 and .NET 4.6.2 C# Windows Forms application. I configured a Connected Service using the Add Service Reference procedure and use the ESTES_Track namespace. Here is my essential code:

ESTES_Track.EstesShipmentTracking_PortTypeClient trackClient = new EstesShipmentTracking_PortTypeClient();
trackClient.ClientCredentials.UserName.UserName = "MYUSERNAME";
trackClient.ClientCredentials.UserName.Password = "MYPASSWORD";
ESTES_Track.search trackSearch = new ESTES_Track.search();
trackSearch.requestID = "TRACK" + DateTime.Now.Ticks.ToString();
trackSearch.pro = "1710394802";
ESTES_Track.shipmentTrackingRequest trackRequest = new shipmentTrackingRequest(trackSearch);
ESTES_Track.shipmentTrackingResponse trackResponse = trackClient.shipmentTracking(trackRequest);

Communication Exception is:

Error in deserializing body of reply message for operation 'shipmentTracking'
There is an error in XML document (1, 575)
   at System.ServiceModel.Dispatcher.XmlSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, XmlSerializer serializer, MessagePartDescription returnPart, MessagePartDescriptionCollection bodyParts, Object[] parameters, Boolean isRequest)
   at System.ServiceModel.Dispatcher.XmlSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)
   at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)
   at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeReply(Message message, Object[] parameters)
   at System.ServiceModel.Dispatcher.ProxyOperationRuntime.AfterReply(ProxyRpc& rpc)
   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at EstesTests.ESTES_Track.EstesShipmentTracking_PortType.shipmentTracking(shipmentTrackingRequest request)
   at EstesTests.ESTES_Track.EstesShipmentTracking_PortTypeClient.shipmentTracking(shipmentTrackingRequest request) in C:\tests\EstesTests\Connected Services\ESTES_Track\Reference.cs:line 1394
   at EstesTests.Program.TrackTest() in C:\tests\EstesTests\Program.cs:line 49

-=-=-=- edit -=-=-=- I can successfully process a Request and get a valid Response using SoapUI. This leads me to believe my problem is specific to my Visual Studio project.

My App.config file looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
    </startup>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="estesrtshipmenttracking_base_ws_provider_soapws_EstesShipmentTracking_Binder">
                  <security mode="Transport">
                    <transport clientCredentialType="Basic"></transport>
                  </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://api.estes-express.com:443/ws/estesrtshipmenttracking.base.ws.provider.soapws:EstesShipmentTracking/estesrtshipmenttracking_base_ws_provider_soapws_EstesShipmentTracking_Port"
                binding="basicHttpBinding" bindingConfiguration="estesrtshipmenttracking_base_ws_provider_soapws_EstesShipmentTracking_Binder"
                contract="ESTES_Track.EstesShipmentTracking_PortType" name="estesrtshipmenttracking_base_ws_provider_soapws_EstesShipmentTracking_Port" />
        </client>
    </system.serviceModel>
</configuration>

-=-=-=- edit -=-=-=- The reason I explicitly labeled this as "ESTES ShipmentTracking v1.1" is that I am quite sure the problem(s) I am having are likely specific to this particular web service. If someone already has a working code sample for using this web service, That might include the solution to my problem. Also, I am sure anyone else trying to develop a shipping client for ESTES will encounter this problem.

-=-=-=- conclusion -=-=-=- I conclude that the XML content of the response messages does not adequately comply with the published v1.1 WSDL schema and that the deserialization exceptions are specific to this web service implementation version. I have not experienced this problem with the previous v1.0 version.

dbc
  • 104,963
  • 20
  • 228
  • 340
gridtrak
  • 731
  • 7
  • 20
  • The is something wrong with the XML response on line 1 character 575. You code may not be waiting for the entire xml before processing. I would use a sniffer like wireshark or fiddler and see what the actual response from the sniffer. – jdweng Jun 13 '19 at 17:08
  • I'll try, but I think the XML may be encrypted by SSL on the wire – gridtrak Jun 13 '19 at 18:07
  • Then the sniffer will indicate that it is encrypted. I do not think so because the error would be at character position one, not at character 575. – jdweng Jun 13 '19 at 18:23
  • When I look at that position in the raw XML response captured when sending the same request using SoapUI, I see the following XML element with spaces in the value: `2019-05-31T19:00:00-0400 ` I think I need to somehow tell the deserializer how to trim those spaces to fix this. – gridtrak Jun 13 '19 at 18:41
  • I hacked the WCF Connected Service code files and changed the DateTime type to string. This "moved" the error to a later character position. After changing all of the DateTime, date, and time types to string, I now get a `trackingInfo` reflection error. Anyway, I don't think hacking the code generator files is a solution, especially if it doesn't work. – gridtrak Jun 13 '19 at 19:39
  • Xml ignores spaces except if there are in a tag name or an attribute name so I do not think the space are the issue. I would compare the working Request with the non working request using the sniffer. Then make the request in c# look exactly like the working request. – jdweng Jun 13 '19 at 23:55
  • When I view the raw data in the Response received by SoapUI using a hex editor, the eventTimeStamp has a trailing TAB character, not spaces like it appeared to have. I will try sniffing the XML, but I suspect it will be SSL encrypted. Time for me to learn WCF Message Logging which I think has options to log the XML content. – gridtrak Jun 14 '19 at 20:33
  • OK, WCF Message Logging works well. The Request and Response are transmitted and received OK. The problem is Deserializing the XML Response after it is received OK from WCF. I cannot see anything wrong with the XML messages in the Service Trace Log Viewer XML display... except for the trailing spaces (which I know to be a TAB) in the eventTimeStamp value. – gridtrak Jun 14 '19 at 21:00
  • Would I get a Deserialization error if there is a problem with the web service's WSDL not mating with the message content? like mis-matched data typing? – gridtrak Jun 14 '19 at 21:04
  • I suspect the issue is with the non standard time format. As a quick test change the class property from DateTime to string and see if you still have an issue. To validate the xml use Visual studio menu : Project : Add New Item : Xml file. Then paste xml into view. Error will show in the Error List like any compiler error. – jdweng Jun 14 '19 at 21:21
  • The spaces in the date are not showing in your response. I can parse the date copying the date above. – jdweng Jun 14 '19 at 21:28
  • Thanks, I'll try the test you suggested. The DateTime value in the raw xml message has a trailing TAB, not spaces. I also notice in the raw xml response message, one of the values contains 50 spaces and it is specified as a data type in the WSDL to be 30 spaces maximum. Would this cause a Deserialization failure? – gridtrak Jun 14 '19 at 22:31
  • The raw XML message between the `` tags validated OK. When I change the `DateTime` type to `string` in the Reference.cs, the Error moves from (1,575) to (1,786). The 786 position is the start of a complex type, whose properties include the 50 spaces value for a type the WSDL specifies should be 30 max. – gridtrak Jun 14 '19 at 22:51
  • OK, I finally managed to get past the deserializing error by changing the WSDL's non-complex custom data types from `type="dx:customtype"` to `type="xsd:string"` and using My hacked WSDL file instead of the file downloaded from the ESTES service. Now, all I need to do is do the same thing again from a fresh ESTES WSDL and find the custom types that do not deserialize. – gridtrak Jun 14 '19 at 23:28
  • Thank you jdweng for your guidance! My conclusion is my question has no answer. I think everything is working as it should. In this case the Deserialization problem occurs because the XML content does not adequately comply with the WSDL schema. If it were only a few XML elements, I would consider hacking the WSDL temporarily acceptable - but not in this case. Also, I don't want to introduce non-original WSDL data types into the Request XML I'm sending to the web service. I will wait for the next version release before trying again. – gridtrak Jun 15 '19 at 00:16
  • Any update or response from Estes? I'm experiencing the same thing! – ultravelocity Jun 20 '19 at 14:14

1 Answers1

2

I experienced the same problem today and narrowed it down to a couple issues:

  • The eventTimeStamp is missing a colon in the timezone portion
  • Empty fields are missing xsi:nil

I wrote to tech support, but in the meanwhile was able to get it to work by manipulating the response with a custom message inspector as described here https://blogs.msdn.microsoft.com/dsnotes/2015/04/14/wcf-simple-way-to-modify-serialized-response/.

You can add it to your client like this:

trackClient.Endpoint.Behaviors.Add(new EstesTrackingEndpointBehavior());

Hopefully they can address this on their end. If other issues come up, you can add additional changes to the response in AfterReceiveReply:

using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Xml;

namespace EstesWebService {
    public class EstesTrackingMessageInspector : IClientMessageInspector {
        public void AfterReceiveReply(ref Message reply, object correlationState) {
            var doc = new XmlDocument();
            var ms = new MemoryStream(); 
            var writer = XmlWriter.Create(ms);
            reply.WriteMessage(writer);
            writer.Flush();
            ms.Position = 0;
            doc.Load(ms);

            //fix the XML
            addNil(doc.SelectNodes(".//shipments"));
            foreach (XmlNode node in doc.SelectNodes(".//eventTimeStamp"))
                fixDateTimeFormat(node);

            ms.SetLength(0);
            writer = XmlWriter.Create(ms);
            doc.WriteTo(writer);
            writer.Flush();
            ms.Position = 0;
            var reader = XmlReader.Create(ms);
            reply = Message.CreateMessage(reader, int.MaxValue, reply.Version);
        }

        public object BeforeSendRequest(ref Message request, IClientChannel channel) {
            return null;
        }

        private void addNil(XmlNodeList nodes) {
            foreach (XmlNode node in nodes) {
                if (node.HasChildNodes)
                    addNil(node.ChildNodes);
                else if (string.IsNullOrWhiteSpace(node.InnerText) && node.Attributes != null && node.Attributes.GetNamedItem("xsi:nil") == null) {
                    var attr = node.OwnerDocument.CreateAttribute("xsi", "nil", "http://www.w3.org/2001/XMLSchema-instance");
                    attr.Value = "true";
                    node.Attributes.SetNamedItem(attr);
                }
            }
        }

        private void fixDateTimeFormat(XmlNode node) {
            if (node != null && !string.IsNullOrWhiteSpace(node.InnerText)) {
                DateTimeOffset dt;
                if (DateTimeOffset.TryParse(node.InnerText.Trim(), out dt))
                    node.InnerText = dt.ToString("O");
            }
        }

    }

    public class EstesTrackingEndpointBehavior : IEndpointBehavior {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) {
            clientRuntime.MessageInspectors.Add(new EstesTrackingMessageInspector());
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) {
        }

        public void Validate(ServiceEndpoint endpoint) {
        }
    }
}
ultravelocity
  • 2,099
  • 16
  • 17