只要问题在于%2F未转义为“/”,就有解决方案。其中一个涉及黑客技巧,对于较新版本的.Net,则需要使用app.config设置。请查看此处:
如何使System.Uri不在路径中取消转义%2f(斜杠)?
然而,我仍然需要弄清楚如何防止它取消转义一些特定转义字符,例如'('和')'(%28和%29)。我已经尝试了所有设置和黑客技巧,以防止Uri类提供部分取消转义的WebRequest路径。这些解决方案可以很好地防止取消转义%2F,但无法防止取消转义%28和%29以及可能大多数其他特定转义字符。
似乎WebRequest正在专门从Uri对象中请求1个值来创建“GET /path HTTP/1.1”语法:Uri.PathAndQuery,它再次调用其UriParser.GetComponents。
如果您想从mediafire下载并且其中包含%28和%29字符,则会进入无限重定向循环,因为.Net会不断将%28和%29更改为'('和')'并跟随重定向(异常:“尝试了太多自动重定向”)。
如果您被卡住了,找不到防止某些字符反转义的方法,那么这是一个解决方案。
目前我惟一发现能够重写此功能(目前使用 .Net 4.6),并提供自己的 PathAndQuery 的方法是通过继承 UriParser 并对其使用进行修改。
public sealed class MyUriParser : System.UriParser
{
private UriParser _originalParser;
private MethodInfo _getComponentsMethod;
public MyUriParser(UriParser originalParser) : base()
{
if (_originalParser == null)
{
_originalParser = originalParser;
_getComponentsMethod = typeof(UriParser).GetMethod("GetComponents", BindingFlags.NonPublic | BindingFlags.Instance);
if (_getComponentsMethod == null)
{
throw new MissingMethodException("UriParser", "GetComponents");
}
}
}
private static Regex rx = new Regex(@"^(?<Scheme>[^:]+):(?://((?<User>[^@/]+)@)?(?<Host>[^@:/?#]+)(:(?<Port>\d+))?)?(?<Path>([^?#]*)?)?(\?(?<Query>[^#]*))?(#(?<Fragment>.*))?$",RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.Singleline);
private Match m = null;
protected override string GetComponents(Uri uri, UriComponents components, UriFormat format)
{
var original = (string)_getComponentsMethod.Invoke(_originalParser, BindingFlags.InvokeMethod, null, new object[] { uri, components, format }, null);
if (components == UriComponents.PathAndQuery)
{
var reg = rx.Match(uri.OriginalString);
var path = reg.Groups["Path"]?.Value;
var query = reg.Groups["Query"]?.Value;
if (path != null && query != null) return $"{path}?{query}";
if (query == null) return $"{path}";
return $"{path}";
}
return original;
}
}
然后通过将其UriParser替换为此实例,将其黑客入Uri实例中。
public static Uri CreateUri(string url)
{
var uri = new Uri(url);
if (url.Contains("%28") || url.Contains("%29"))
{
var originalParser = ReflectionHelper.GetValueByReflection(uri, "m_Syntax") as UriParser;
var parser = new MyUriParser(originalParser);
ReflectionHelper.SetValueByReflection(parser, "m_Scheme", "http");
ReflectionHelper.SetValueByReflection(parser, "m_Port", 80);
ReflectionHelper.SetValueByReflection(uri, "m_Syntax", parser);
}
return uri;
}
由于UriParser的工作方式,通常需要注册以设置其端口和方案名称,因此这两个值必须通过反射设置,因为我们没有以正确的方式进行注册。我还没有找到注册“http”的方法,因为它已经存在。ReflectionHelper只是我拥有的一个类,但可以很快地用普通的反射代码替换它。
然后像这样调用它:
HttpWebRequest dataRequest = (HttpWebRequest)WebRequest.Create(CreateUri(serviceURL));