Uri.TryCreate genera UriFormatException?

Ho un metodo che tenta di creare un Uri e quindi ripulirlo (rimuove i frammenti, esclude alcuni domini e modelli di stringhe di query, ecc.). Il metodo assomiglia a questo:

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); } 

Questo metodo ha funzionato bene per mesi. Ma ieri sera ha fallito. Uri.TryCreate () ha lanciato un’eccezione! Ecco la traccia dello stack:

 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) 

La documentazione di Uri.TryCreate(Uri, String, out Uri) dice che il valore restituito è True se ha esito positivo, False altrimenti, ma è silenzioso sulle eccezioni. Tuttavia, la documentazione di Uri.TryCreate(Uri, Uri, out Uri) dice:

Questo metodo costruisce l’URI, lo inserisce in forma canonica e lo convalida. Se si verifica un’eccezione non gestita, questo metodo lo rileva. Se vuoi creare un Uri e ottenere delle eccezioni usa uno dei costruttori di Uri.

La traccia dello stack mostra che l’eccezione è stata lanciata in Uri.TryCreate(Uri, Uri, out Uri) , che, secondo la documentazione, non dovrebbe accadere.

Questo è un evento molto raro. Ho usato quel codice per mesi, eseguendo letteralmente miliardi di URL attraverso di esso, e fino ad ora non ho riscontrato alcun problema. Sfortunatamente non so quale combinazione di cose abbia causato il problema. Spero di build un caso di test che mostri l’errore.

Si tratta di un bug noto in Uri.TryCreate o mi manca qualcosa?

Non volendo aspettare diversi mesi prima che il mio codice possa incontrare di nuovo questa situazione, ho trascorso un po ‘di tempo con ILDASM per capire cosa sta facendo TryCreate , e poi un altro po’ di tempo per trovare un modo per riprodurre l’errore.

La causa del crash in Uri.TryCreate(Uri baseUri, Uri relativeUri, out Uri result) sembra essere una baseUri mal formattata. Ad esempio, il costruttore Uri consente quanto segue:

 Uri badUri = new Uri("mailto:[email protected]@mischel.com"); 

Secondo l’RFC per mailto: URI, questo non dovrebbe essere permesso. E anche se il costruttore crea e restituisce un object Uri , provando ad accedere (alcune delle) sue proprietà genera UriFormatException . Ad esempio, dato il codice precedente, questa riga genererà un’eccezione:

 string badUriString = badUri.AbsoluteUri; 

Trovo piuttosto interessante il fatto che la class Uri sembra utilizzare due diversi algoritmi di analisi: uno utilizzato durante la costruzione e uno utilizzato internamente per ottenere i singoli componenti.

Passare questo Uri non valido a TryCreate comporterà l’eccezione che ho descritto nella domanda originale. Il metodo TryCreate controlla il parametro baseUri per null , ma non lo può (non posso, lo immaginerei) convalidarlo altrimenti. Si deve supporre che, se il parametro non è nullo, l’object passato è un’istanza Uri pienamente inizializzata e valida. Ma a un certo punto nella costruzione del risultato, TryCreate tenta di ottenere i componenti di baseUri e viene generata un’eccezione.

Non posso dire che il mio programma abbia effettivamente incontrato un mailto: URL che è stato formattato in questo modo. Posso dire con un certo grado di certezza, tuttavia, che un object Uri non valido è stato la causa del crash nel mio programma, semplicemente perché la traccia dello stack delle eccezioni dal mio programma corrisponde alla traccia dello stack dal programma di test. In poche parole, il bug si trova nel costruttore Uri (e anche nei metodi TryCreate ) che consente di creare l’ Uri non valido.

È ansible seguire la segnalazione di bug su Microsoft Connect.

Ora che sai che può fallire, prendiamo ulteriori informazioni:

 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 InalidOperationException( String.Format("Can create URI for base={0}, rel={1}", baseUri.ToString(), relstr), ex); } return CleanupUri(result, out result); } 
  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; }