I have this code below that creates the query parameter collection for a restful web service call
NameValueCollection outgoingQueryString = HttpUtility.ParseQueryString(String.Empty);
outgoingQueryString.Add("SessionID", _sessionId);
outgoingQueryString.Add("LoanNumberID", loanId.ToString());
outgoingQueryString.Add("Nonce", _nonce);
outgoingQueryString.Add("OtherParams", "Enable_Filter_Show_on_Action_Report=0");
The problem is that the last parameter value Enable_Filter_Show_on_Action_Report=0 get's it's '=' encoded to %3d SessionID=319A561B6D&LoanNumberID=351591&Nonce=3262383361&OtherParams=Enable_Filter_Show_on_Action_Report%3d0
when it should be
SessionID=319A561B6D&LoanNumberID=351591&Nonce=3262383361&OtherParams=Enable_Filter_Show_on_Action_Report=0
short of doing a rewrite after the fact is there a way to prevent this from happening? The service does not return the proper results set because it interprets the %3d as something it needs to filter.
What I did to fix the issue was the following. I just didn't know if there was a way to prevent this in the first place.
string queryString = outgoingQueryString.ToString().Replace("%3d", "=");
I don't know of a way to get NameValueCollection
to do what you want; you're probably stuck with a workaround of some sort.
I do see a potential problem in your workaround implementation:
[Fact]
public void ProblemWithOriginalSolution()
{
NameValueCollection outgoingQueryString = HttpUtility.ParseQueryString(String.Empty);
outgoingQueryString.Add("Param1", "Value1");
outgoingQueryString.Add("Param2", "Value2=Something"); // this '=' needs to remain URL-encoded
outgoingQueryString.Add("OtherParams", "Enable_Filter_Show_on_Action_Report=0");
var queryString = outgoingQueryString.ToString().Replace("%3d", "=");
Assert.Contains("Value2=Something", queryString); // Passes, but not what we want
Assert.Contains("Value2%3dSomething", queryString); // Actually what we want, but fails
}
That might not matter for your needs, but if it does, I have two suggestions.
The first is to just use string concatentation. Depending on your use, this might need extra logic to handle the case where OtherParams
is the only query string parameter (the ampersand should be omitted in that case). String concatenation is also a bad idea of OtherParams
might contain characters that need to be URL encoded.
[Fact]
public void Concatentation()
{
NameValueCollection outgoingQueryString = HttpUtility.ParseQueryString(String.Empty);
outgoingQueryString.Add("Param1", "Value1");
var queryString = outgoingQueryString.ToString() + "&OtherParams=Enable_Filter_Show_on_Action_Report=0";
Assert.Contains("&OtherParams=Enable_Filter_Show_on_Action_Report=0", queryString);
}
The second is to do a double-substitution. This is a bit heavy-handed, and not terribly efficient, but should be reliable.
[Fact]
public void DoubleSubstitution()
{
// Make sure the placeholder doesn't have any characters that will get URL encoded!
// Also make sure it's not something that could appear in your input.
const string equalsPlaceholder = "__Equals__";
NameValueCollection outgoingQueryString = HttpUtility.ParseQueryString(String.Empty);
outgoingQueryString.Add("Param1", "Value1");
// First, remove the equals signs from the OtherParams value
outgoingQueryString.Add("OtherParams", "Enable_Filter_Show_on_Action_Report=0".Replace("=", equalsPlaceholder));
// Then, build the query string, and substitute the equals signs back in
var queryString = outgoingQueryString.ToString().Replace(equalsPlaceholder, "=");
Assert.Contains("&OtherParams=Enable_Filter_Show_on_Action_Report=0", queryString);
}