Как создать экземпляр Uri, обработанный с помощью GenericUriParserOptions.DontCompressPath
Когда класс .NET System.Uri
анализирует строки, он выполняет некоторую нормализацию на входе, например, нижнюю оболочку схемы и имя хоста. Он также обрезает периоды возврата из каждого сегмента пути. Эта последняя функция является фатальной для приложений OpenID, потому что некоторые OpenID (например, выпущенные из Yahoo) включают в себя сегменты пути с кодировкой base64, которые могут заканчиваться периодом.
Как отключить это поведение обрезки в классе Uri?
Регистрация моей собственной схемы с помощью UriParser.Register
с синтаксическим анализатором, инициализированным с помощью GenericUriParserOptions.DontCompressPath
, позволяет избежать обрезки периода и некоторых других операций, которые также нежелательны для OpenID. Но я не могу зарегистрировать новый парсер для существующих схем, таких как HTTP и HTTPS, которые я должен сделать для OpenID.
Другой подход, который я пробовал, - это зарегистрировать мою собственную новую схему и программировать пользовательский парсер, чтобы изменить схему на стандартные схемы HTTP (s) как часть разбора:
public class MyUriParser : GenericUriParser
{
private string actualScheme;
public MyUriParser(string actualScheme)
: base(GenericUriParserOptions.DontCompressPath)
{
this.actualScheme = actualScheme.ToLowerInvariant();
}
protected override string GetComponents(Uri uri, UriComponents components, UriFormat format)
{
string result = base.GetComponents(uri, components, format);
// Substitute our actual desired scheme in the string if it in there.
if ((components & UriComponents.Scheme) != 0)
{
string registeredScheme = base.GetComponents(uri, UriComponents.Scheme, format);
result = this.actualScheme + result.Substring(registeredScheme.Length);
}
return result;
}
}
class Program
{
static void Main(string[] args)
{
UriParser.Register(new MyUriParser("http"), "httpx", 80);
UriParser.Register(new MyUriParser("https"), "httpsx", 443);
Uri z = new Uri("httpsx://me.yahoo.com/b./c.#adf");
var req = (HttpWebRequest)WebRequest.Create(z);
req.GetResponse();
}
}
Это практически работает. Экземпляр Uri
сообщает https вместо httpsx всюду - кроме самого свойства Uri.Scheme. Это проблема, когда вы передаете этот экземпляр Uri
в HttpWebRequest
для отправки запроса на этот адрес. По-видимому, он проверяет свойство Scheme и не распознает его как "https", потому что он просто отправляет открытый текст в 443-порт вместо SSL.
Я рад за любое решение, которое:
- Сохраняет конечные периоды в сегментах пути в
Uri.Path
- Включает эти периоды в исходящие HTTP-запросы.
- В идеале работает с поддержкой среднего уровня ASP.NET(но не обязательно).
Ответы
Ответ 1
Microsoft заявляет, что она будет исправлена в .NET 4.0 (хотя по комментариям она еще не исправлена)
https://connect.microsoft.com/VisualStudio/feedback/details/386695/system-uri-incorrectly-strips-trailing-dots?wa=wsignin1.0#tabs
Однако на этой странице есть обходной путь. Это связано с использованием рефлексии для изменения параметров, поэтому, возможно, это не соответствует требованиям среднего доверия. Просто прокрутите страницу вниз и щелкните вкладку "Обходные пути".
Спасибо jxdavis и Google за этот ответ:
http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/5206beca-071f-485d-a2bd-657d635239c9
Ответ 2
Мне любопытно, если часть проблемы состоит в том, что вы учитываете только "не сжимаете путь", а не все значения по умолчанию базового анализатора HTTP: (включая UnEscapeDotsAndSlashes)
private const UriSyntaxFlags HttpSyntaxFlags = (UriSyntaxFlags.AllowIriParsing | UriSyntaxFlags.AllowIdn | UriSyntaxFlags.UnEscapeDotsAndSlashes | UriSyntaxFlags.CanonicalizeAsFilePath | UriSyntaxFlags.CompressPath | UriSyntaxFlags.ConvertPathSlashes | UriSyntaxFlags.PathIsRooted | UriSyntaxFlags.AllowAnInternetHost | UriSyntaxFlags.AllowUncHost | UriSyntaxFlags.MayHaveFragment | UriSyntaxFlags.MayHaveQuery | UriSyntaxFlags.MayHavePath | UriSyntaxFlags.MayHavePort | UriSyntaxFlags.MayHaveUserInfo | UriSyntaxFlags.MustHaveAuthority);
Это, в отличие от новостей с флагами (например):
private const UriSyntaxFlags NewsSyntaxFlags = (UriSyntaxFlags.AllowIriParsing | UriSyntaxFlags.MayHaveFragment | UriSyntaxFlags.MayHavePath);
Данг, Брэндон Блэк избил меня, пока я работал над тем, чтобы набирать вещи...
Это может помочь с чтением кода:
namespace System
{
[Flags]
internal enum UriSyntaxFlags
{
AllowAnInternetHost = 0xe00,
AllowAnyOtherHost = 0x1000,
AllowDnsHost = 0x200,
AllowDOSPath = 0x100000,
AllowEmptyHost = 0x80,
AllowIdn = 0x4000000,
AllowIPv4Host = 0x400,
AllowIPv6Host = 0x800,
AllowIriParsing = 0x10000000,
AllowUncHost = 0x100,
BuiltInSyntax = 0x40000,
CanonicalizeAsFilePath = 0x1000000,
CompressPath = 0x800000,
ConvertPathSlashes = 0x400000,
FileLikeUri = 0x2000,
MailToLikeUri = 0x4000,
MayHaveFragment = 0x40,
MayHavePath = 0x10,
MayHavePort = 8,
MayHaveQuery = 0x20,
MayHaveUserInfo = 4,
MustHaveAuthority = 1,
OptionalAuthority = 2,
ParserSchemeOnly = 0x80000,
PathIsRooted = 0x200000,
SimpleUserSyntax = 0x20000,
UnEscapeDotsAndSlashes = 0x2000000,
V1_UnknownUri = 0x10000
}
}
Ответ 3
Вы должны уметь выходить вперед, чтобы избежать ".". используя "% 2E", но это дешевый и грязный выход.
Возможно, вы немного поиграете с опцией dontEscape, и это может изменить то, как Uri обрабатывает эти символы.
Подробнее здесь:
http://msdn.microsoft.com/en-us/library/system.uri.aspx
Также проверьте следующее (см. DontUnescapePathDotsAndSlashes):
http://msdn.microsoft.com/en-us/library/system.genericuriparseroptions.aspx
Ответ 4
Это работает?
public class MyUriParser : UriParser
{
private string actualScheme;
public MyUriParser(string actualScheme)
{
Type type = this.GetType();
FieldInfo fInfo = type.BaseType.GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
fInfo.SetValue(this, GenericUriParserOptions.DontCompressPath);
this.actualScheme = actualScheme.ToLowerInvariant();
}
protected override string GetComponents(Uri uri, UriComponents components, UriFormat format)
{
string result = base.GetComponents(uri, components, format);
// Substitute our actual desired scheme in the string if it in there.
if ((components & UriComponents.Scheme) != 0)
{
string registeredScheme = base.GetComponents(uri, UriComponents.Scheme, format);
result = this.actualScheme + result.Substring(registeredScheme.Length);
}
return result;
}}