Reputation: 1477
Take this example:
var client = new HttpClient();
await client.GetAsync("http://www.google.com?q=%2D");
this actually sends a request to 'http://www.google.com?q=-'. I don't want .NET to alter my url.
This behavior is from System.Uri, which seems to unescape those character.
How can I prevent Uri/HttpClient from changing my url?
.NET Framework 4.7.2
Update: this behavior seems by design. I still can't believe there is not a way around this. What if I actually want to send '?q=what does %2D mean' to google.com? Now this gets send as 'http://www.google.com/?q=what%20does%20-%20mean'. Which is NOT what I meant to do.
Upvotes: 1
Views: 1136
Reputation: 4929
Possible partial solution based on reflection.
I think the problem is that -
is listed as a special character, here: https://referencesource.microsoft.com/#System/net/System/UriHelper.cs,657 . I don't think there's a way to modify the http
scheme to change that behavior.
There was a previous bug, which has since been fixed, relating to how file paths are parsed by Uri
. At the time, the workaround was to change the private flags of the related UriParser
using reflection : https://stackoverflow.com/a/2285321/1462295
Here is a quick demo which you'll have to evaluate if it helps or not. It depends on whether uri.ToString()
is called (then this might help), or uri.GetComponents
(then you'll have to figure something else out). This code reaches into the Uri
object and replaces the parsed string with something else. Here's the code and console output:
static void Main(string[] args)
{
var surl = "http://www.google.com?q=%2D";
var url = new Uri(surl);
Console.WriteLine("Broken: " + url.ToString());
// Declared in Uri class as
// private UriInfo m_Info;
// https://referencesource.microsoft.com/#System/net/System/URI.cs,129
FieldInfo infoField = url.GetType().GetField("m_Info", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
// Immediately after m_Info is declared, the private class definition is given:
// private class UriInfo {
// public string String;
// ...
// };
object info = infoField.GetValue(url);
FieldInfo infoStringField = info.GetType().GetField("String");
// If you check the value of m_Info.String, you'll see it has the
// modified string with '?q=-'.
// The idea with this block of code is to replace the parsed
// string with the one that you want.
// This just replaces the string with the original value.
infoStringField.SetValue(info, surl);
// ToString() @ https://referencesource.microsoft.com/#System/net/System/URI.cs,1661
// There are a couple of 'if' branches, but the last line is
// return m_Info.String;
// This is the idea behind the above code.
Console.WriteLine("Fixed: " + url.ToString());
// However, GetComponents uses entirely different logic:
Console.WriteLine($"Still broken: {url.GetComponents(UriComponents.AbsoluteUri, UriFormat.Unescaped)}");
Console.WriteLine($"Still broken: {url.GetComponents(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped)}");
Console.WriteLine($"Still broken: {url.GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped)}");
Console.WriteLine("Press ENTER to exit ...");
Console.ReadLine();
}
Console output:
Broken: http://www.google.com/?q=-
Fixed: http://www.google.com?q=%2D
Still broken: http://www.google.com/?q=-
Still broken: http://www.google.com/?q=-
Still broken: http://www.google.com/?q=-
Press ENTER to exit ...
You might find some other inspiration from the code here which does use reflection, but also defines its own scheme to work with. Note the trust issues mentioned.
You mention .Net Framework 4.7.2, which should work with the above code. dotnet core will not.
Upvotes: 1