C # Comment puis-je vérifier si une URL existe / est valide?


117

Je crée un programme simple en visuel c # 2005 qui recherche un symbole boursier sur Yahoo! Finance, télécharge les données historiques, puis trace l'historique des prix pour le symbole boursier spécifié.

Je connais l'URL exacte dont j'ai besoin pour acquérir les données, et si l'utilisateur entre un symbole boursier existant (ou au moins un avec des données sur Yahoo! Finance), cela fonctionne parfaitement. Cependant, j'ai une erreur d'exécution si l'utilisateur crée un symbole boursier, car le programme tente d'extraire des données d'une page Web inexistante.

J'utilise la classe WebClient et la fonction DownloadString. J'ai parcouru toutes les autres fonctions membres de la classe WebClient, mais je n'ai rien vu que je pourrais utiliser pour tester une URL.

Comment puis-je faire ceci?


1
mise à jour pour afficher l'utilisation de C # 2.0 (VS2005)
Marc Gravell

Réponses:


110

Vous pourriez émettre une demande "HEAD" plutôt qu'un "GET"?

(modifier) ​​- lol! On dirait que j'ai déjà fait ça ! changé en wiki pour éviter les accusations de collecte de représentants. Donc, pour tester une URL sans le coût du téléchargement du contenu:

// using MyClient from linked post
using(var client = new MyClient()) {
    client.HeadOnly = true;
    // fine, no content downloaded
    string s1 = client.DownloadString("http://google.com");
    // throws 404
    string s2 = client.DownloadString("http://google.com/silly");
}

Vous devriez try/ catchautour du DownloadStringpour vérifier les erreurs; pas d'erreur? Ça existe...


Avec C # 2.0 (VS2005):

private bool headOnly;
public bool HeadOnly {
    get {return headOnly;}
    set {headOnly = value;}
}

et

using(WebClient client = new MyClient())
{
    // code as before
}

FWIW - Je ne sais pas si cela résout vraiment le problème (autre que peut-être un comportement différent côté client) car vous changez simplement la méthode HTTP. La réponse du serveur dépendra fortement de la façon dont la logique est codée et peut ne pas fonctionner correctement pour un service dynamique comme le cours des actions. Pour les ressources statiques (par exemple, images, fichiers, etc.) HEAD fonctionne généralement comme annoncé car il est intégré au serveur. De nombreux programmeurs n'effectuent pas explicitement les requêtes HEAD car l'accent est normalement mis sur POST et GET. YMMV
David Taylor

Désolé d'avoir mis si longtemps à choisir une réponse ... J'ai été distrait par l'école et le travail et j'ai en quelque sorte oublié ce post. En passant, je ne pouvais pas tout à fait faire fonctionner votre solution car j'utilise Visual Studio 2005 qui n'a pas le type «var». Je n'ai pas travaillé sur ce projet depuis des mois, mais y a-t-il une solution simple à ce fait? De plus, lorsque j'ai essayé d'implémenter votre solution, je me souviens que cela m'a énervé d'avoir essayé de définir la propriété HeadOnly sans code dans les définitions «get» et «set». Ou peut-être que je faisais juste quelque chose de mal. Merci pour l'aide en tout cas!
Daniel Waltrip

Qu'est-ce que MyClient ?
Kiquenet le

@Kiquenet il y a un lien dans le corps, vers ici: stackoverflow.com/questions/153451/...
Marc Gravell

136

Voici une autre implémentation de cette solution:

using System.Net;

///
/// Checks the file exists or not.
///
/// The URL of the remote file.
/// True : If the file exits, False if file not exists
private bool RemoteFileExists(string url)
{
    try
    {
        //Creating the HttpWebRequest
        HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
        //Setting the Request method HEAD, you can also use GET too.
        request.Method = "HEAD";
        //Getting the Web Response.
        HttpWebResponse response = request.GetResponse() as HttpWebResponse;
        //Returns TRUE if the Status code == 200
        response.Close();
        return (response.StatusCode == HttpStatusCode.OK);
    }
    catch
    {
        //Any exception will returns false.
        return false;
    }
}

De: http://www.dotnetought.net/2009/10/14/how-to-check-remote-file-exists-using-c/


2
J'utilise ce code pour vérifier si un tas d'images existe, et c'est assez lent (quelques secondes par URL). Est-ce que quelqu'un sait si c'est un problème avec ce code, ou simplement une réalité lors de ces types d'appels?
ssmith

@ssmith Une façon d'accélérer votre code est de faire la vérification dans une boucle Parallel.Foreach si vous ne l'avez pas encore essayé. Cela a rendu mon application de test d'URL BEAUCOUP plus rapide.
Jack Fairfield

3
Ce truc lance DisposedObject en retour (response.StatusCode == HttpStatusCode.OK); wrap in using
Lapenkov Vladimir

1
Il y a un problème avec le code ci-dessus. si vous faites response.Close (); alors vous ne pouvez pas vérifier la réponse.StatusCode car il est fermé, il lancera une exception.
Renascent le

@ssmith une méthode beaucoup plus rapide?
Kiquenet le

36

Ces solutions sont plutôt bonnes, mais elles oublient qu'il peut y avoir d'autres codes d'état que 200 OK. C'est une solution que j'ai utilisée sur des environnements de production pour la surveillance de l'état et autres.

S'il y a une redirection d'url ou une autre condition sur la page cible, le retour sera vrai en utilisant cette méthode. De plus, GetResponse () lèvera une exception et vous n'obtiendrez donc pas de StatusCode pour cela. Vous devez intercepter l'exception et rechercher une ProtocolError.

Tout code d'état 400 ou 500 renverra faux. Tous les autres retournent vrai. Ce code est facilement modifié pour répondre à vos besoins de codes d'état spécifiques.

/// <summary>
/// This method will check a url to see that it does not return server or protocol errors
/// </summary>
/// <param name="url">The path to check</param>
/// <returns></returns>
public bool UrlIsValid(string url)
{
    try
    {
        HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
        request.Timeout = 5000; //set the timeout to 5 seconds to keep the user from waiting too long for the page to load
        request.Method = "HEAD"; //Get only the header information -- no need to download any content

        using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
        {
            int statusCode = (int)response.StatusCode;
            if (statusCode >= 100 && statusCode < 400) //Good requests
            {
                return true;
            }
            else if (statusCode >= 500 && statusCode <= 510) //Server Errors
            {
                //log.Warn(String.Format("The remote server has thrown an internal error. Url is not valid: {0}", url));
                Debug.WriteLine(String.Format("The remote server has thrown an internal error. Url is not valid: {0}", url));
                return false;
            }
        }
    }
    catch (WebException ex)
    {
        if (ex.Status == WebExceptionStatus.ProtocolError) //400 errors
        {
            return false;
        }
        else
        {
            log.Warn(String.Format("Unhandled status [{0}] returned for url: {1}", ex.Status, url), ex);
        }
    }
    catch (Exception ex)
    {
        log.Error(String.Format("Could not test url {0}.", url), ex);
    }
    return false;
}

1
J'ajouterais que certains codes de statut de la gamme 3xx provoqueront en fait une erreur, par exemple 304 Non modifié, auquel cas vous devriez le gérer dans votre bloc catch
RobV

3
Je viens de rencontrer un problème avec cette approche: HttpWebRequestje n'aime pas ça si vous ne faites pas .Close()l' responseobjet avant d'essayer de télécharger autre chose. Il a fallu des heures pour trouver celui-là!
jbeldock

4
HttpWebResponseL'objet doit être enfermé dans un usingbloc car il implémente IDisposablece qui assurera également la fermeture de la connexion. Cela pourrait poser des problèmes comme @jbeldock, a été confronté.
Habib

2
Il lance 404 Not Founds sur des URL qui fonctionnent bien dans un navigateur ...?
Michael Tranchida

Les serveurs Web @MichaelTranchida sont notoirement connus pour 404 lorsque vous émettez une méthode qui n'est pas prise en charge. Dans votre cas Head, cette ressource Getpourrait ne pas être prise en charge. Il aurait dû lancer 405 à la place.
Sriram Sakthivel

9

Si je comprends bien votre question, vous pouvez utiliser une petite méthode comme celle-ci pour vous donner les résultats de votre test d'URL:

WebRequest webRequest = WebRequest.Create(url);  
WebResponse webResponse;
try 
{
  webResponse = webRequest.GetResponse();
}
catch //If exception thrown then couldn't get response from address
{
  return 0;
} 
return 1;

Vous pouvez envelopper le code ci-dessus dans une méthode et l'utiliser pour effectuer la validation. J'espère que cela répond à la question que vous posiez.


1
Oui, vous pouvez peut-être affiner la solution en différenciant les différents cas (échec de connexion TCP - l'hôte refuse la connexion, 5xx - un événement fatal s'est produit, 404 - ressource non trouvée, etc.). Jetez un œil à la propriété Status de WebException;)
David Taylor

Très bon point David! Cela nous donnerait des commentaires plus détaillés afin que nous puissions gérer l'erreur plus astucieusement.
Logiciel de calendrier

1
Merci. Mon point est qu'il y a plusieurs couches à cet oignon, dont chacune peut jeter une clé dans les travaux (.Net Framework, résolution DNS, connectivité TCP, serveur Web cible, application cible, etc.). À mon humble avis, une bonne conception devrait être en mesure de faire la distinction entre les différentes conditions de défaillance pour fournir des commentaires informatifs et des diagnostics utilisables. N'oublions pas non plus que HTTP a des codes de statut pour une raison;)
David Taylor

6

Essayez ceci (assurez-vous d'utiliser System.Net):

public bool checkWebsite(string URL) {
   try {
      WebClient wc = new WebClient();
      string HTMLSource = wc.DownloadString(URL);
      return true;
   }
   catch (Exception) {
      return false;
   }
}

Lorsque la fonction checkWebsite () est appelée, elle essaie d'obtenir le code source de l'URL qui lui est passé. S'il obtient le code source, il renvoie true. Sinon, il renvoie false.

Exemple de code:

//The checkWebsite command will return true:
bool websiteExists = this.checkWebsite("https://www.google.com");

//The checkWebsite command will return false:
bool websiteExists = this.checkWebsite("https://www.thisisnotarealwebsite.com/fakepage.html");

3

Voici une autre option

public static bool UrlIsValid(string url)
{
    bool br = false;
    try {
        IPHostEntry ipHost = Dns.Resolve(url);
        br = true;
    }
    catch (SocketException se) {
        br = false;
    }
    return br;
}

3
Cela peut être utile pour vérifier si un hôte existe. La question n'est évidemment pas de savoir si l'hôte existe ou non. Il s'agit de gérer un mauvais chemin HTTP étant donné que l'hôte est connu pour exister et fonctionner correctement .
binki

3

Cette solution semble facile à suivre:

public static bool isValidURL(string url) {
    WebRequest webRequest = WebRequest.Create(url);
    WebResponse webResponse;
    try
    {
        webResponse = webRequest.GetResponse();
    }
    catch //If exception thrown then couldn't get response from address
    {
        return false ;
    }
    return true ;
}

1
n'oubliez pas de fermer webResponse, sinon le temps de réponse augmentera chaque fois que vous appelez votre méthode
Madagaga

3
WebRequest request = WebRequest.Create("http://www.google.com");
try
{
     request.GetResponse();
}
catch //If exception thrown then couldn't get response from address
{
     MessageBox.Show("The URL is incorrect");`
}

1
Veuillez ajouter quelques explications à votre réponse. Les réponses basées uniquement sur le code ont tendance à prêter à confusion et à ne pas être utiles pour les futurs lecteurs et peuvent ainsi attirer des votes négatifs.
Jesse

2

J'ai un moyen plus simple de déterminer la météo qu'une URL est valide.

if (Uri.IsWellFormedUriString(uriString, UriKind.RelativeOrAbsolute))
{
   //...
}

4
Non, cette méthode ne vérifie pas si l'url est vraiment accessible. Il renvoie même vrai lorsque Uri.IsWellFormedUriString (" 192.168.1.421 ", ...), qui utilise une URL manifestement incorrecte
zhaorufei

2

J'ai toujours trouvé que les exceptions sont beaucoup plus lentes à être traitées.

Peut-être qu'une manière moins intensive permettrait d'obtenir un meilleur résultat plus rapide?

public bool IsValidUri(Uri uri)
{

    using (HttpClient Client = new HttpClient())
    {

    HttpResponseMessage result = Client.GetAsync(uri).Result;
    HttpStatusCode StatusCode = result.StatusCode;

    switch (StatusCode)
    {

        case HttpStatusCode.Accepted:
            return true;
        case HttpStatusCode.OK:
            return true;
         default:
            return false;
        }
    }
}

Ensuite, utilisez simplement:

IsValidUri(new Uri("http://www.google.com/censorship_algorithm"));

1

Les serveurs Web répondent avec un code d'état HTTP indiquant le résultat de la demande, par exemple 200 (parfois 202) signifie succès, 404 - non trouvé, etc. (voir ici ). En supposant que la partie adresse du serveur de l'URL est correcte et que vous n'obtenez pas de délai d'expiration de socket, l'exception vous indique très probablement que le code d'état HTTP était différent de 200. Je suggérerais de vérifier la classe de l'exception et de voir si l'exception porte le code d'état HTTP.

IIRC - L'appel en question lève une WebException ou un descendant. Vérifiez le nom de la classe pour voir lequel et encapsulez l'appel dans un bloc try pour intercepter la condition.


2
En fait, tout ce qui se situe dans la fourchette 200-299 est synonyme de succès, IIRC
Marc Gravell

Marc, vous avez absolument raison. J'ai volontairement évité d'entrer dans le concept de «classe d'erreur» (par exemple 5xx, 4xx, 3xx, 2xx, etc.) car cela ouvre une toute autre boîte de vers. Même la gestion des codes standard (200, 302, 404, 500, etc.) est bien meilleure que d'ignorer complètement les codes.
David Taylor

1

À la suite des exemples déjà donnés, je dirais, il est préférable d'envelopper également la réponse dans une utilisation comme celle-ci

    public bool IsValidUrl(string url)
    {
         try
         {
             var request = WebRequest.Create(url);
             request.Timeout = 5000;
             request.Method = "HEAD";

             using (var response = (HttpWebResponse)request.GetResponse())
             {
                response.Close();
                return response.StatusCode == HttpStatusCode.OK;
            }
        }
        catch (Exception exception)
        { 
            return false;
        }
   }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.