¿Una mejor manera de validar URL en C # que try-catch?


Estoy construyendo una aplicación para recuperar una imagen de Internet. A pesar de que funciona bien, es lento (en una URL dada incorrecta) cuando se utilizan sentencias try-catch en la aplicación.

(1) ¿Es esta la mejor manera de verificar la URL y manejar la entrada incorrecta, o debería usar Regex (o algún otro método) en su lugar?

(2) ¿Por qué la aplicación intenta encontrar imágenes localmente si no especifico http:// en el cuadro de texto?

private void btnGetImage_Click(object sender, EventArgs e)
{
    String url = tbxImageURL.Text;
    byte[] imageData = new byte[1];

    using (WebClient client = new WebClient())
    {
        try
        {
            imageData = client.DownloadData(url);
            using (MemoryStream ms = new MemoryStream(imageData))
            {
                try
                {
                    Image image = Image.FromStream(ms);
                    pbxUrlImage.Image = image;
                }
                catch (ArgumentException)
                {
                    MessageBox.Show("Specified image URL had no match", 
                        "Image Not Found", MessageBoxButtons.OK, 
                        MessageBoxIcon.Error);
                }
            }
        }
        catch (ArgumentException)
        {
            MessageBox.Show("Image URL can not be an empty string", 
                "Empty Field", MessageBoxButtons.OK, 
                MessageBoxIcon.Information);
        }
        catch (WebException)
        {
            MessageBox.Show("Image URL is invalid.\nStart with http:// " +
                "and end with\na proper image extension", "Not a valid URL",
                MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    } // end of outer using statement
} // end of btnGetImage_Click

EDITAR: Probé la solución sugerida por Panagiotis Kanavos (gracias por tu esfuerzo!), pero solo queda atrapado en la sentencia if-else si el usuario ingresa http:// y nada más. Cambiando a UriKind.Absolute atrapa cuerdas vacías también! Acercando :) El código a partir de ahora:

private void btnGetImage_Click(object sender, EventArgs e)
{
    String url = tbxImageURL.Text;
    byte[] imageData = new byte[1];
    Uri myUri;

    // changed to UriKind.Absolute to catch empty string
    if (Uri.TryCreate(url, UriKind.Absolute, out myUri))
    {
        using (WebClient client = new WebClient())
        {
            try
            {
                imageData = client.DownloadData(myUri);
                using (MemoryStream ms = new MemoryStream(imageData))
                {
                    imageData = client.DownloadData(myUri);
                    Image image = Image.FromStream(ms);
                    pbxUrlImage.Image = image;
                }
            }
            catch (ArgumentException)
            {
                MessageBox.Show("Specified image URL had no match",
                    "Image Not Found", MessageBoxButtons.OK, 
                    MessageBoxIcon.Error);
            }
            catch (WebException)
            {
                MessageBox.Show("Image URL is invalid.\nStart with http:// " +
                    "and end with\na proper image extension", 
                    "Not a valid URL",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
    }
    else
    {
        MessageBox.Show("The Image Uri is invalid.\nStart with http:// " +
            "and end with\na proper image extension", "Uri was not created",
            MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

Debo estar haciendo algo mal aquí. :(

Author: Cœur, 2010-07-12

9 answers

Use Uri.TryCreate para crear un nuevo objeto Uri solo si la cadena de url es una URL válida. Si la cadena no es una URL válida, TryCreate devuelve false.

string myString = "http://someUrl";
Uri myUri;
if (Uri.TryCreate(myString, UriKind.RelativeOrAbsolute, out myUri))
{
    //use the uri here
}

UPDATE

TryCreate o el constructor Uri aceptarán felizmente cadenas que pueden parecer inválidas, por ejemplo "Host: www.stackoverflow.com", "Host:%20www.stackoverflow.com"or" chrome:about " (en inglés). De hecho, estos son URI perfectamente válidos que especifican un esquema personalizado en lugar de "http".

El documentación de la Uri .La propiedad Scheme proporciona más ejemplos como "gopher:" (¿alguien recuerda esto?), "news", "mailto", "uuid".

Una aplicación puede registrarse como un controlador de protocolo personalizado como se describe en MSDN u otras preguntas, por ejemplo ¿Cómo registro un protocolo de URL personalizado en Windows?

TryCreate no proporciona una manera de restringirse a esquemas específicos. El código necesita comprobar el Uri.Propiedad de Scheme para asegurarse de que contiene un valor aceptable

ACTUALIZACIÓN 2

Pasar una cadena extraña como "></script><script>alert(9)</script> devolverá true y construirá un objeto Uri relativo. Llamando a Uri.Sin embargo, IsWellFormedOriginalString devolverá false. Por lo tanto, probablemente necesite llamar a IsWellFormedOriginalString si desea asegurarse de que los URI relativos estén bien formados.

Por otro lado, llamar TryCreate con UriKind.Absolute devolverá false en este caso.

Curiosamente, Uri.IsWellFormedUriString llama a TryCreate internamente y luego devuelve el valor de IsWellFormedOriginalString si se creó un Uri relativo.

 107
Author: Panagiotis Kanavos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:00:20

Un atajo sería usar Uri .IsWellFormedUriString :

if (Uri.IsWellFormedUriString(myURL, UriKind.RelativeOrAbsolute))
...
 49
Author: Todd Menier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-01 21:26:35

Algunos ejemplos al usar Uri para probar una URL válida fallan

Uri myUri = null;
if (Uri.TryCreate("Host: www.stackoverflow.com", UriKind.Absolute, out myUri))
{
}

  myUri = null;
if (Uri.TryCreate("Accept: application/json, text/javascript, */*; q=0.01", UriKind.Absolute, out myUri))
{
}

  myUri = null;
if (Uri.TryCreate("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0", UriKind.Absolute, out myUri))
{
}

  myUri = null;
if (Uri.TryCreate("DNT: 1", UriKind.Absolute, out myUri))
{
}

Me sorprendió que todas estas tonterías aparecieran en mi listview después de validar con lo anterior. Pero todo pasa la prueba de validación.

Ahora añado lo siguiente después de la validación anterior

url = url.ToLower();
if (url.StartsWith("http://") || url.StartsWith("https://")) return true;
 5
Author: Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-19 19:10:44

Hola validas https http,ftp,sftp,ftps,cualquier cosa que comience con www.

string regular = @"^(ht|f|sf)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*)?$";
string regular123 = @"^(www.)[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*)?$";

string myString = textBox1.Text.Trim();
if (Regex.IsMatch(myString, regular))
{
    MessageBox.Show("It is valide url  " + myString);
}
else if (Regex.IsMatch(myString, regular123))
{
    MessageBox.Show("Valide url with www. " + myString);
}
else 
{
    MessageBox.Show("InValide URL  " + myString);
}
 4
Author: Naren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-23 10:14:00

O este código fuente buena imagen optimización válida:

 public static string ValidateImage(string absoluteUrl,string defaultUrl)
        { 
           Uri myUri=null; 
           if (Uri.TryCreate(absoluteUrl, UriKind.Absolute, out myUri))
            {
                using (WebClient client = new WebClient())
                {
                    try
                    {
                        using (Stream stream = client.OpenRead(myUri))
                        {
                            Image image = Image.FromStream(stream);
                            return (image != null) ? absoluteUrl : defaultUrl;
                        }
                    }
                    catch (ArgumentException)
                    {
                        return defaultUrl;
                    }
                    catch (WebException)
                    {
                        return defaultUrl;
                    }
                }
            }
            else
            {
                return defaultUrl;
            }
        }

Sou y demo asp.net mvc imagen fuente creada:

<img src="@ValidateImage("http://example.com/demo.jpg","nophoto.png")"/>
 3
Author: Elyor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-01 10:05:19

Mi solución:

string regular = @"^(ht|f|sf)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&amp;%\$#_]*)?$";
string myString = textBox1.Text.Trim();
if (Regex.IsMatch(myString, regular))
{
    MessageBox.Show("it is valide url  " + myString);
}
else
{
    MessageBox.Show("InValide url  " + myString);
}
 3
Author: Naren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-22 13:45:11

Usarlo.....

string myString = http//:google.com;
Uri myUri;
Uri.TryCreate(myString, UriKind.RelativeOrAbsolute, out myUri);
 if (myUri.IsAbsoluteUri == false)
 {
  MessageBox.Show("Please Input Valid Feed Url");
 }
 3
Author: Shivam Srivastava,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-01 10:25:07

Puede usar la función Uri.TryCreate Como Panagiotis Kanavos sugerida si desea probar y crear una url o puede usar la función Uri.IsWellFormedUriString como sugiere Todd Menier si solo desea probar la validez de la Url. esto puede ser útil si solo está validando la entrada del usuario por ahora y necesita crear una url algún tiempo después en el tiempo de vida de su aplicación.

* * Pero mi post es para la gente, como yo: (, todavía golpeando sus cabezas contra. net 1.1 * *

Ambos los métodos anteriores se introdujeron en. net 2.0, por lo que todavía tienen que usar el método try catch, que, en mi opinión, sigue siendo mucho mejor que usar expresiones regulares.

private bool IsValidHTTPURL(string url)
{
    bool result = false;

    try
    {
        Uri uri = new Uri(url);

        result = (uri.Scheme == "http" || uri.Scheme == "https");
    }
    catch (Exception ex) 
    { 
        log.Error("Exception while validating url", ex); 
    }

    return result;
}
 2
Author: Mubashar Ahmad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:33:35

Quería comprobar si la url también contiene una extensión de dominio, debe ser una url de sitio web válida.

Esto es lo que se me ocurrió:

 public static bool IsValidUrl(string url)
        {
            if (string.IsNullOrEmpty(url)) { return false;}

            if (!url.StartsWith("http://"))
            {
                url = "http://" + url;    
            }

            Uri outWebsite;

            return Uri.TryCreate(url, UriKind.Absolute, out outWebsite) && outWebsite.Host.Replace("www.", "").Split('.').Count() > 1 && outWebsite.HostNameType == UriHostNameType.Dns && outWebsite.Host.Length > outWebsite.Host.LastIndexOf(".") + 1 && 255 >= url.Length;
        }

He probado el código con linqpad:

    void Main()
{
        // Errors
        IsValidUrl("www.google/cookie.png").Dump();
        IsValidUrl("1234").Dump();
        IsValidUrl("abcdef").Dump();
        IsValidUrl("abcdef/test.png").Dump();
        IsValidUrl("www.org").Dump();
        IsValidUrl("google").Dump();
        IsValidUrl("google.").Dump();
        IsValidUrl("google/test").Dump();
        IsValidUrl("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0").Dump();
        IsValidUrl("</script><script>alert(9)</script>").Dump();
        IsValidUrl("Accept: application/json, text/javascript, */*; q=0.01").Dump();
        IsValidUrl("DNT: 1").Dump();

        Environment.NewLine.Dump();

        // Success
        IsValidUrl("google.nl").Dump();
        IsValidUrl("www.google.nl").Dump();
        IsValidUrl("http://google.nl").Dump();
        IsValidUrl("http://www.google.nl").Dump();
}

Resultados:

False False False False False False False False False False

Verdadero Verdadero Verdadero Verdadero

 1
Author: Jamie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-07 16:06:18