4

(I'm using a .Net 2.0 Web Service instead of WCF, because my needs are simple and I want full Mono compatibility...)

Ok, I'm a C#/Visual Studio novice, so I apologize for the basic question.

The SOAP service I'm communicating with provides a WSDL, and I used it to create .Net Web Service references. So far, so good.

However, the service expects the payload to be an XML string, like so (an example...)

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <Search xmlns="http://foo.bar/bazservice/">
      <session>ABCDEF0123456789</session>
      <options>

<![CDATA[<Search>
  <SearchConditions>
    <Condition>
      <Field>1234</Field>
      <Value>Test Record</Value>
    </Condition>
  </SearchConditions>
  <Fields>
    <Field>5678</Field>
  </Fields>
</Search>]]>

      </options>
    </Search>
  </soap:Body>
</soap:Envelope>

So I call my function using the generated code that visual studio was nice enough to magic for me, like this:

  // Build an XML thing, and encapsulate it in a CDATA
  XElement searchQuery = this.BuildSearchXml(params, search);
  XCData cdata = new XCData(searchQuery.ToString());

  // XML String looks good...
  Console.WriteLine(cdata.ToString() + Environment.NewLine);

  // Let's send XML payload to service...
  search client = this.Connect("search");

  // Search() expects the second argument to be an XML string...
  var returnVal = client.Search(session, cdata.ToString(), 1);

  return returnVal.ToString();

Unfortunately, the web service glue code, somewhere in the magic that was generated, automatically urlencodes the XML string!

When doing some tcpdump investigation, here's the payload as it's actually sent:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <Search xmlns="http://foo.bar/bazservice/">
      <session>ABCDEF0123456789</session>
      <options>

&lt;![CDATA[&lt;Search&gt;
  &lt;SearchConditions&gt;
    &lt;Condition&gt;
      &lt;Field&gt;1234&lt;/Field&gt;
      &lt;Value&gt;Test Record&lt;/Value&gt;
    &lt;/Condition&gt;
  &lt;/SearchConditions&gt;
  &lt;Fields&gt;
    &lt;Field&gt;5678&lt;/Field&gt;
  &lt;/Fields&gt;
&lt;/Search&gt;]]&gt;

      </options>
    </Search>
  </soap:Body>
</soap:Envelope>

My question is this: how do I keep using the Web Reference magic as it was generated with the WSDL, but tell it to not URLEncode my raw XML string, but send it as is?!

I'm quite the novice - I'm looking at the generated code (in .\Web References\MyService\Reference.cs), but I'm at a loss.

Thanks in advance for any help in pointing me in the right direction!!!

...

edit: added code for Reference.cs

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

//
// This source code was auto-generated by Microsoft.VSDesigner, Version 4.0.30319.42000.
//
#pragma warning disable 1591

namespace MyService.Search {
    using System;
    using System.Web.Services;
    using System.Diagnostics;
    using System.Web.Services.Protocols;
    using System.Xml.Serialization;
    using System.ComponentModel;

    // ..... snip other stuff ..... //

    /// <remarks/>
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.6.1055.0")]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Web.Services.WebServiceBindingAttribute(Name="searchSoap", Namespace="http://foo.bar/bazservice")]
    public partial class search : System.Web.Services.Protocols.SoapHttpClientProtocol {

        [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://foo.bar/bazservice/Search", RequestNamespace="http://foo.bar/bazservice", ResponseNamespace="http://foo.bar/bazservice", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
        public string ExecuteSearch(string sessionToken, string searchOptions, int pageNumber) {
            object[] results = this.Invoke("Search", new object[] {
                        sessionToken,
                        searchOptions,
                        pageNumber});
            return ((string)(results[0]));
        }

        public void ExecuteSearchAsync(string sessionToken, string searchOptions, int pageNumber) {
            this.ExecuteSearchAsync(sessionToken, searchOptions, pageNumber, null);
        }

        // ..... snip other methods ..... //
    }
}

#pragma warning restore 1591
Joshua Pruitt
  • 134
  • 2
  • 11

1 Answers1

1

That's not URL-encoding. That's XML encoding (entity references). It is usually going to be functionally equivalent to the CDATA block. Don't worry about the formatting - it's doing exactly as it should. Just make sure the service is behaving as it should.

https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references

Xavier J
  • 4,326
  • 1
  • 14
  • 25
  • Thanks for the clarification. The problem is, the service **breaks** when it receives XML-encoded payload. It expects a *raw* XML payload (I've tested this in Python and got it to work, BTW). I don't have any control over the service - it's a proprietary deal I have to interact with, so even if it's breaking the rules, I have to break the rules on my side to compensate. Thanks for your time! – Joshua Pruitt May 09 '16 at 16:22
  • I took another look. You are double-encoding. First, you're embedding it in a CDATA element. Then, the serializer is encoding it again. Try it without the CDATA wrapping (just drop in the "searchQuery" value directly to your service call). – Xavier J May 09 '16 at 16:24
  • Ok, I'll try that real quick, hang tight. – Joshua Pruitt May 09 '16 at 16:27
  • 1
    Without the CDATA element, just passing in searchQuery.ToString(), it does the same thing (XML-encodes). So, no dice. – Joshua Pruitt May 09 '16 at 16:28
  • Is the searchQuery value well-formatted XML? Also, I'm a little suspicious of the fact that you have a Search element inside of another Search element. If all else fails, try using SoapUI outside of the IDE. https://smartbear.com/product/ready-api/soapui-ng/overview/ – Xavier J May 09 '16 at 16:32
  • Yeah. I build it of nested XElements, then convert it to string. It's solid, and what the API documentation expects (and I'm able to send it "raw" in Python and the service works flawlessly). Cool tool, I'm checking it out now. Thanks! – Joshua Pruitt May 09 '16 at 16:38