125 votes

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

Je suis en train de créer un programme simple en Visual 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 saisit un symbole de téléscripteur existant (ou au moins un symbole avec des données sur Yahoo ! Finance), cela fonctionne parfaitement bien. Cependant, j'ai une erreur d'exécution si l'utilisateur invente un symbole de téléscripteur, 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 puisse utiliser pour tester une URL.

Comment puis-je le faire ?

1 votes

Mise à jour pour montrer l'utilisation de C# 2.0 (VS2005)

147voto

BigJoe714 Points 3189

Voici une autre mise en œuvre 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.dotnetthoughts.net/2009/10/14/how-to-check-remote-file-exists-using-c/

2 votes

J'utilise ce code pour vérifier l'existence d'une série d'images, et il est assez lent (quelques secondes par URL). Quelqu'un sait-il s'il s'agit d'un problème lié à ce code ou simplement d'un fait de la vie quand on fait ce genre d'appels ?

0 votes

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

3 votes

Ce truc jette DisposedObject dans return (response.StatusCode == HttpStatusCode.OK) ; wrap in using

119voto

Marc Gravell Points 482669

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

(edit) - lol ! On dirait que j'ai a déjà fait cela auparavant ! ; changé en wiki pour éviter les accusations de "rep-garnering". Ainsi, 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");
}

Tu voudrais try / catch autour de la DownloadString pour vérifier les erreurs ; pas d'erreur ? Il 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
}

0 votes

Pour information - Je ne suis pas sûr que cela résolve vraiment le problème (à part peut-être un comportement différent côté client) puisque 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 de l'action. Pour les ressources statiques (par exemple, les images, les fichiers, etc.), HEAD fonctionne généralement comme prévu puisqu'il est intégré au serveur. De nombreux programmeurs n'utilisent pas explicitement les requêtes HEAD car ils se concentrent généralement sur les requêtes POST et GET. YMMV

0 votes

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 message. En passant, je n'ai pas réussi à faire fonctionner votre solution parce que j'utilise Visual Studio 2005 qui n'a pas le type 'var'. Je n'ai pas travaillé sur ce projet depuis des mois, mais existe-t-il un correctif simple pour ce fait ? De plus, lorsque j'ai essayé de mettre en œuvre votre solution, je me souviens qu'elle s'est mise en colère contre moi parce que j'essayais de définir la propriété HeadOnly sans aucun code dans les définitions 'get' et 'set'. Ou peut-être que je faisais simplement quelque chose de mal. Mais merci pour votre aide !

0 votes

Qu'est-ce que MonClient ?

38voto

jsmith Points 445

Ces solutions sont plutôt bonnes, mais elles oublient qu'il peut y avoir d'autres codes d'état que 200 OK. Il s'agit d'une solution que j'ai utilisée dans des environnements de production pour le suivi 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 cette méthode. Vous devez piéger l'exception et rechercher une ProtocolError.

Tout code d'état 400 ou 500 renvoie false. Tous les autres renvoient vrai. Ce code peut être facilement modifié pour répondre à vos besoins en matière 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 votes

J'ajouterais que certains codes d'état de l'ordre de 3xx provoquent en fait une erreur, par exemple 304 Not Modified, auquel cas vous devez traiter cette erreur dans votre bloc catch.

3 votes

Je viens de faire l'expérience d'un problème d'arrachage de cheveux avec cette approche : HttpWebRequest n'aime pas que tu ne le fasses pas. .Close() le site response avant d'essayer de télécharger autre chose. J'ai mis des heures à trouver celui-là !

4 votes

HttpWebResponse objet doivent être entourés de using bloc puisqu'il met en œuvre IDisposable qui assurera également la fermeture de la connexion. Cela peut causer des problèmes comme ceux rencontrés par @jbeldock.

9voto

Si je comprends bien votre question, vous pourriez 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 pourriez 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 votes

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

0 votes

Très bon point David ! Cela nous permettrait d'avoir un retour d'information plus détaillé afin de pouvoir traiter l'erreur de manière plus astucieuse.

1 votes

Merci. Ce que je veux dire, c'est qu'il y a plusieurs couches à cet oignon, chacune d'entre elles pouvant jeter une pierre à l'édifice (.Net Framework, résolution DNS, connectivité TCP, serveur Web cible, application cible, etc.) ). Une bonne conception devrait être capable de distinguer les différentes conditions de défaillance afin de fournir un retour d'information et un diagnostic utilisable. N'oublions pas non plus que le HTTP a des codes d'état pour une raison ;)

3voto

abobjects.com Points 59

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 votes

N'oubliez pas de fermer webResponse, sinon le temps de réponse augmentera à chaque fois que vous appellerez votre méthode

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X