5

In an ASP.NET application in which users ("User A") can set up their own web service connections using SOAP, I let them insert their own envelope, which, for example, could be something along these lines:

//Formatted for Clarity
string soapMessage = 
"<soap: Envelope //StandardStuff>
  <soap:Header //StandardStuff>
    <wsse:UsernameToken>
      <wsse: Username>{F1}</wsse:Username>
      <wsse: Password Type''>{F2}</wsse:Password>  
    </wsse:UsernameToken>
  </soap:Header>
  <soap:Body>
    <ref:GetStuff>
      <ref:IsActive>{F3}</ref:IsActive>
    </ref:GetStuff>
  </soap:Body>
</soap:Envelope>"

At the same time I a "User B" that sends an array of data, passed down from Javascript as json that looks a little something like this:

[
  { 
    key: "F1", 
    value: "A" 
  },
  { 
    key: "F2", 
    value: "B" 
  },
  { 
    key: "F3", 
    value: "C" 
  }
];

This array enters the fray as a string before being deserialized (dynamic JsonObject = JsonConvert.DeserializeObject(stringifiedJson);).

Now, I would like to be able to insert the corresponding values into the envelope, preferably with a degree of security that won't allow people to do funky stuff by inserting weird values in the array (a regex would probably be my last resort).

So far I'm aware of the concept to build the string like so (With the {}'s in the soap message being replaced by {0}, {1} & {2}):

string value1 = "A";
string value2 = "B";
string value3 = "C";
var body = string.Format(@soapMessage, value1, value2, value3);

request.ContentType = "application/soap+xml; charset=utf-8";
request.ContentLength = body.Length;
request.Accept = "text/xml";
request.GetRequestStream().Write(Encoding.UTF8.GetBytes(body), 0, body.Length);

But the amount of values in this array as well as the might change according to the user's input as well as a shifting order of references, so I need something more flexible. I'm very new to making SOAP calls, so as dumb an answer as possible would be appreciated.

Patrick
  • 302
  • 2
  • 19
  • Have you evaluated any existing templating engines like `nustache` (https://github.com/jdiamond/Nustache) or `handlebars.net` (https://github.com/rexm/Handlebars.Net)? – Unglückspilz Oct 26 '17 at 21:52
  • I have not since I am not at liberty to add new libraries to this project. As such an option where these are not required would have preference. – Patrick Oct 30 '17 at 08:18

3 Answers3

1

Consider creating a function that would replace the placeholders with their respective values.

private string FormatMessage(string soapMessage, IDictionary<string, string> parameters) {
    var message = soapMessage;
    foreach (var kvp in parameters) {
        var placeholder = "{" + kvp.Key + "}";
        if (message.IndexOf(placeholder) > -1) {
            message = message.Replace(placeholder, kvp.Value);
        }
    }
    return message;
}

Where the dictionary was extracted from JSON supplied to the function.

var parameters = JsonConvert.DeserializeObject<IDictionary<string, string>>(json);

string body = FormatMessage(soapMessage, parameters);

You would however need to validate the values and keys provided to avoid injections that could adversely affect your system.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Would you have any suggestions on how to validate these values? The problem is that this concerns one user supplying a message for another user to fill in, so there is no prior knowledge as to what this message or values might contain. – Patrick Oct 30 '17 at 10:54
  • @Patrick, Those are implementation concerns that you would need to handle before transforming the message. And given that you are the in control of the code you can always verify that the provided values match for the current user before trying to transform the message. – Nkosi Oct 30 '17 at 11:05
1

It is always a good idea to process xml using xml processors instead of string replacement. My cross-thinking idea might not exactly fit your usecase, i didnt get your big picture 100% i have to admit.

Anyway my Answer is that you could use xpaths as key which would enable you to utilize xml processing tools on the backend. You do not neccessarily have to validate anything at this point, this is very dependent on the architecture.

So in my mind, javascript provides this structure:

[
  { 
    key: "//family-name", 
    value: "Michael" 
  },
  { 
    key: "//nickname", 
    value: "Jackson" 
  }
];

And the backend:

XmlElement foo = (XmlElement)doc.SelectSingleNode(key);
foo.InnerText = value;
Harry
  • 1,233
  • 10
  • 24
  • The idea is that the soap-message is provided by user A and the values are provided by user B, as such it might be complicated to get user A to think in xml-selectors, but I absolutely love your out-of-the-box idea. – Patrick Nov 01 '17 at 10:21
0

The best tool for this job, IMHO, is XSLT:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:crs="CRS610MI" exclude-result-prefixes="crs">
<xsl:output method="xml"/>
<xsl:template match="/">
  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <soap:Body>
    <xsl:apply-templates select="node()|@*"/>
    </soap:Body>
  </soap:Envelope>
</xsl:template>
<xsl:template match="node()|@*">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

There is a post on SO regarding this specific topic:

Wrap XML in SOAP envelope in .net

.NET Supports XSLT out of the box, so you will need no third party libraries. I would load the JSON into an XML object, and apply that to the XSLT template which you will modify to respect your in memory XML structure.

Use an XmlLoader in C# to load your JSON into an XML object:

XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
string jsonText = JsonConvert.SerializeXmlNode(doc);

// To convert JSON text contained in string json into an XML node
XmlDocument doc = JsonConvert.DeserializeXmlNode(json);

Loading an XSLT template in .NET is pretty straight forward:

var xml = XDocument.Load(JsonReaderWriterFactory.CreateJsonReader(
Encoding.ASCII.GetBytes(jsonString), new XmlDictionaryReaderQuotas()));

This is a clean and professional approach that I believe matches your criteria.

Shawn J. Molloy
  • 2,457
  • 5
  • 41
  • 59