Uri.TryCreate выбрасывает UriFormatException?
У меня есть метод, который пытается создать Uri, а затем очистить его (удаляет фрагменты, исключает некоторые домены и шаблоны строк запроса и т.д.). Метод выглядит следующим образом:
static public bool TryCreateCleanUri(Uri baseUri, string relstr, out Uri result)
{
if (!Uri.TryCreate(baseUri, relstr, out result))
{
return false;
}
return CleanupUri(result, out result);
}
Этот метод работает отлично в течение нескольких месяцев. Но прошлой ночью это провалилось. Uri.TryCreate() выбрасывает исключение! Здесь трассировка стека:
ERROR: Unhandled exception caught. Program terminating.
System.UriFormatException: Invalid URI: The hostname could not be parsed.
at System.Uri.CreateHostStringHelper(String str, UInt16 idx, UInt16 end, Flags& flags, String& scopeId)
at System.Uri.CreateHostString()
at System.Uri.GetComponentsHelper(UriComponents uriComponents, UriFormat uriFormat)
at System.Uri.CombineUri(Uri basePart, String relativePart, UriFormat uriFormat)
at System.Uri.GetCombinedString(Uri baseUri, String relativeStr, Boolean dontEscape, String& result)
at System.Uri.ResolveHelper(Uri baseUri, Uri relativeUri, String& newUriString, Boolean& userEscaped, UriFormatException& e)
at System.Uri.TryCreate(Uri baseUri, Uri relativeUri, Uri& result)
at System.Uri.TryCreate(Uri baseUri, String relativeUri, Uri& result)
Документация для Uri.TryCreate(Uri, String, out Uri)
говорит о том, что в противном случае возвращаемое значение True
, False
в противном случае, но оно умолчание об исключениях. Однако в документации для Uri.TryCreate(Uri, Uri, out Uri)
указано:
Этот метод создает URI, помещает он в канонической форме и проверяет Это. Если возникает необработанное исключение, этот метод ловит его. Если ты хочешь создать использование Uri и получить исключения один из конструкторов Uri.
Трассировка стека показывает, что исключение было выбрано в Uri.TryCreate(Uri, Uri, out Uri)
, которое, согласно документации, не должно происходить.
Это очень редкое явление. Я использую этот код в течение нескольких месяцев, пробегая буквально миллиарды URL-адресов через него и до сих пор не сталкивался с проблемой. К сожалению, я не знаю, какая комбинация вещей вызвала проблему. Я надеюсь построить тестовый пример, который показывает ошибку.
Является ли это известной ошибкой в Uri.TryCreate
, или я что-то не хватает?
Ответы
Ответ 1
Не желая ждать несколько месяцев, чтобы мой код снова столкнулся с этой ситуацией, я провел некоторое время с ILDASM, чтобы выяснить, что делает TryCreate
, а затем немного больше времени, чтобы воспроизвести ошибку.
Причиной аварии в Uri.TryCreate(Uri baseUri, Uri relativeUri, out Uri result)
представляется плохо отформатированный baseUri
. Например, конструктор Uri
допускает следующее:
Uri badUri = new Uri("mailto:[email protected]@mischel.com");
В соответствии с RFC для mailto: URI, это не должно быть разрешено. И хотя конструктор создает и возвращает объект Uri
, пытаясь получить доступ (некоторые из) его свойств, бросает UriFormatException
. Например, учитывая приведенный выше код, эта строка генерирует исключение:
string badUriString = badUri.AbsoluteUri;
Мне кажется довольно интересным, что класс Uri
, по-видимому, использует два разных алгоритма синтаксического анализа: один используется во время построения и один используется для получения отдельных компонентов.
Передача этого недопустимого Uri
в TryCreate
приведет к исключению, которое я описал в исходном вопросе. Метод TryCreate
проверяет параметр baseUri
для null
, но не может (я не мог бы представить) проверить его иначе. Он должен предположить, что если параметр не является нулевым, переданный объект является полностью инициализированным и действительным экземпляром Uri
. Но в какой-то момент при построении результата TryCreate
пытается получить компоненты baseUri
и возникает исключение.
Я не могу сказать, что моя программа действительно столкнулась с mailto: URL-адресом, который был отформатирован таким образом. Я могу с некоторой долей уверенности сказать, что недопустимый Uri
объект был причиной сбоя в моей программе, просто потому, что трассировка стека исключений из моей программы соответствует трассировке стека из тестовой программы. Проще говоря, ошибка находится в конструкторе Uri
(а также в методах TryCreate
), которые позволяют создать недействительный Uri
.
Вы можете следить за сообщением об ошибке в Microsoft Connect.
Ответ 2
Теперь, когда вы знаете, что это может дать сбой, давайте получим больше информации:
static public bool TryCreateCleanUri(Uri baseUri, string relstr, out Uri result)
{
try {
if (!Uri.TryCreate(baseUri, relstr, out result))
{
return false;
}
}
catch (UriFormatException ex) {
throw new InvalidOperationException(
String.Format("Can create URI for base={0}, rel={1}", baseUri.ToString(), relstr),
ex);
}
return CleanupUri(result, out result);
}
Ответ 3
public static bool CheckUrlValid(string url)
{
Uri uriResult;
bool result = Uri.TryCreate(url, UriKind.Absolute, out uriResult);
if(result)
{
uriResult = new Uri(url);
if (uriResult.Scheme == Uri.UriSchemeHttps || uriResult.Scheme == Uri.UriSchemeHttp)
return true;
}
return false;
}