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.

2
  • Have you evaluated any existing templating engines like nustache (github.com/jdiamond/Nustache) or handlebars.net (github.com/rexm/Handlebars.Net)? Commented Oct 26, 2017 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. Commented Oct 30, 2017 at 8:18

3 Answers 3

1
+50

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.

Sign up to request clarification or add additional context in comments.

2 Comments

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, 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.
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;

1 Comment

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.
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.