C # Jak mogę sprawdzić, czy adres URL istnieje / jest prawidłowy?


117

Tworzę prosty program w Visual C # 2005, który wyszukuje symbol giełdowy na Yahoo! Finance, pobiera dane historyczne, a następnie wykreśla historię cen dla określonego symbolu giełdowego.

Znam dokładny adres URL, którego potrzebuję, aby uzyskać dane, a jeśli użytkownik wprowadzi istniejący symbol giełdowy (lub przynajmniej jeden z danymi w Yahoo! Finance), działa to doskonale. Jednak pojawia się błąd w czasie wykonywania, jeśli użytkownik tworzy symbol giełdowy, ponieważ program próbuje pobrać dane z nieistniejącej strony internetowej.

Używam klasy WebClient i funkcji DownloadString. Przejrzałem wszystkie inne funkcje składowe klasy WebClient, ale nie znalazłem niczego, czego mógłbym użyć do przetestowania adresu URL.

W jaki sposób mogę to zrobić?


1
zaktualizowano w celu pokazania użycia C # 2.0 (VS2005)
Marc Gravell

Odpowiedzi:


110

Możesz wysłać żądanie „HEAD” zamiast „GET”?

(edytuj) - lol! Wygląda na to, że robiłem to wcześniej ! zmieniono na wiki, aby uniknąć oskarżeń o zbieranie reputacji. Aby więc przetestować adres URL bez kosztów pobierania treści:

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

Będziesz try/ catchwokół, DownloadStringaby sprawdzić błędy; żaden błąd? Istnieje...


W języku C # 2.0 (VS2005):

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

i

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

FWIW - Nie jestem pewien, czy to naprawdę rozwiązuje problem (poza być może innym zachowaniem po stronie klienta), ponieważ po prostu zmieniasz metodę HTTP. Odpowiedź z serwera będzie w dużym stopniu zależeć od sposobu kodowania logiki i może nie działać dobrze w przypadku usługi dynamicznej, takiej jak cena akcji. W przypadku zasobów statycznych (np. Obrazów, plików itp.) HEAD zwykle działa zgodnie z reklamą, ponieważ jest zapisywany na serwerze. Wielu programistów nie wysyła wprost żądań HEAD, ponieważ zwykle koncentrują się na POST i GET. YMMV
David Taylor

Przepraszam, że tak długo trwało wybieranie odpowiedzi ... Zostałem zboczony ze szkoły i pracy i trochę zapomniałem o tym poście. Na marginesie, nie mogłem uruchomić twojego rozwiązania, ponieważ używam Visual Studio 2005, który nie ma typu „var”. Nie pracowałem nad tym projektem od miesięcy, ale czy jest na to proste rozwiązanie? Również kiedy próbowałem zaimplementować Twoje rozwiązanie, pamiętam, że wściekło się na mnie za próbę zdefiniowania właściwości HeadOnly bez kodu w definicjach „get” i „set”. A może po prostu robiłem coś złego. Ale dzięki za pomoc!
Daniel Waltrip

Co to jest MyClient ?
Kiquenet

@Kiquenet jest link w treści, do tutaj: stackoverflow.com/questions/153451/ ...
Marc Gravell

136

Oto kolejna realizacja tego rozwiązania:

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

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


2
Używam tego kodu, aby sprawdzić, czy istnieje kilka obrazów i działa dość wolno (kilka sekund na adres URL). Czy ktoś wie, czy to jest problem z tym kodem, czy po prostu fakt z życia podczas wykonywania tego typu połączeń?
ssmith

@ssmith Jednym ze sposobów na przyspieszenie kodu jest sprawdzenie w pętli Parallel.Foreach, jeśli jeszcze tego nie próbowałeś. To znacznie przyspieszyło moją aplikację do testowania adresów URL.
Jack Fairfield

3
Ta rzecz zwraca w zamian DisposedObject (response.StatusCode == HttpStatusCode.OK); wrap in using
Lapenkov Vladimir

1
Wystąpił problem z powyższym kodem. jeśli odpowiesz.Close (); wtedy nie możesz sprawdzić odpowiedzi.StatusCode ponieważ jest blisko, zgłosi wyjątek.
Renascent

@ssmith jakaś metoda znacznie szybsza?
Kiquenet

36

Te rozwiązania są całkiem dobre, ale zapominają, że mogą istnieć inne kody stanu niż 200 OK. Jest to rozwiązanie, którego używałem na środowiskach produkcyjnych do monitorowania statusu i tym podobnych.

Jeśli na stronie docelowej znajduje się przekierowanie adresu URL lub inny warunek, zwrot przy użyciu tej metody będzie prawdziwy. Ponadto GetResponse () zgłosi wyjątek i dlatego nie otrzymasz dla niego StatusCode. Musisz przechwycić wyjątek i sprawdzić ProtocolError.

Każdy kod statusu 400 lub 500 zwróci fałsz. Wszyscy inni zwracają prawdę. Ten kod można łatwo zmodyfikować, aby dopasować go do potrzeb dotyczących określonych kodów statusu.

/// <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
Dodam, że niektóre kody statusu z zakresu 3xx faktycznie spowodują wyrzucenie błędu np. 304 Not Modified, w takim przypadku powinieneś sobie z tym
poradzić

3
Właśnie doświadczył pull-your-hair-out problem z tym podejściem: HttpWebRequestnie podoba, jeśli NIE .Close()ten responseobiekt, zanim spróbujesz ściągnąć cokolwiek innego. Znalezienie tego zajęło wiele godzin!
jbeldock

4
HttpWebResponseobiekt powinien być zamknięty w usingbloku, ponieważ realizuje IDisposableco również zapewni zamknięcie połączenia. Może to powodować problemy, przed którymi stanął @jbeldock.
Habib

2
Wyrzuca błąd 404 Not Founds w adresach URL, które działają dobrze w przeglądarce ...?
Michael Tranchida

@MichaelTranchida Serwery sieci Web są znane z 404, gdy wydajesz metodę, która nie jest obsługiwana. W twoim przypadku może Headnie być obsługiwany przez ten zasób, chociaż Getmoże być. Zamiast tego powinien wyrzucić 405.
Sriram Sakthivel

9

Jeśli dobrze zrozumiem Twoje pytanie, możesz skorzystać z małej metody, takiej jak ta, aby uzyskać wyniki testu adresu 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;

Możesz zawinąć powyższy kod w metodę i użyć go do przeprowadzenia walidacji. Mam nadzieję, że to odpowiada na pytanie, które zadałeś.


1
Tak, być może możesz udoskonalić rozwiązanie, rozróżniając różne przypadki (awaria połączenia TCP - host odmawia połączenia, 5xx - wydarzyło się coś krytycznego, 404 - nie znaleziono zasobu itp.). Spójrz na właściwość Status WebException;)
David Taylor

Bardzo słuszna uwaga, David! To dałoby nam bardziej szczegółowe informacje, dzięki czemu moglibyśmy wnikliwszy sposób poradzić sobie z błędem.
Oprogramowanie kalendarza

1
Dzięki. Chodzi mi o to, że istnieje kilka warstw tej cebuli, z których każda może wrzucić klucz do prac (.Net Framework, DNS Resolution, TCP Connectivity, docelowy serwer sieci Web, docelowa aplikacja itp.). IMHO dobry projekt powinien być w stanie rozróżnić różne warunki awarii, aby zapewnić informacje zwrotne i użyteczną diagnostykę. Nie zapominajmy też, że HTTP ma kody statusu z jakiegoś powodu;)
David Taylor

6

Spróbuj tego (upewnij się, że używasz System.Net):

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

Gdy wywoływana jest funkcja checkWebsite (), próbuje ona pobrać kod źródłowy przekazanego do niej adresu URL. Jeśli otrzyma kod źródłowy, zwraca wartość true. Jeśli nie, zwraca false.

Przykład kodu:

//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

Oto inna opcja

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
Może to być przydatne do sprawdzenia, czy host istnieje. Pytanie oczywiście nie dotyczy tego, czy host istnieje, czy nie. Dotyczy obsługi złej ścieżki HTTP, biorąc pod uwagę, że host jest znany i działa prawidłowo .
binki

3

To rozwiązanie wydaje się łatwe do naśladowania:

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
nie zapomnij zamknąć webResponse, w przeciwnym razie czas odpowiedzi wydłuży się za każdym razem, gdy zadzwonisz do swojej metody
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
Dodaj wyjaśnienie do swojej odpowiedzi. Odpowiedzi zawierające tylko kod są zwykle mylące i nie są pomocne dla przyszłych czytelników i mogą w ten sposób przyciągać głosy negatywne.
Jesse

2

mam prostszy sposób na sprawdzenie, czy adres URL jest prawidłowy.

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

4
Nie, ta metoda nie sprawdza, czy adres URL jest naprawdę dostępny. Zwraca nawet wartość true, gdy Uri.IsWellFormedUriString (" 192.168.1.421 ", ...), które używają oczywiście niepoprawnego
adresu

2

Zawsze uważałem, że wyjątki są znacznie wolniejsze w obsłudze.

Być może mniej intensywny sposób przyniósłby lepszy i szybszy wynik?

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

Następnie po prostu użyj:

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

1

Serwery WWW odpowiadają kodem statusu HTTP wskazującym wynik żądania, np. 200 (czasami 202) oznacza sukces, 404 - nie znaleziono itp. (Patrz tutaj ). Zakładając, że część adresu URL zawierająca adres serwera jest poprawna i nie otrzymujesz limitu czasu gniazda, najprawdopodobniej wyjątek mówi, że kod statusu HTTP był inny niż 200. Sugerowałbym sprawdzenie klasy wyjątku i sprawdzenie, czy wyjątek przenosi kod stanu HTTP.

IIRC - wywołanie w pytaniu zgłasza WebException lub element podrzędny. Sprawdź nazwę klasy, aby zobaczyć, która z nich i zawiń wywołanie blokiem try, aby przechwycić warunek.


2
Właściwie wszystko z zakresu 200-299 oznacza sukces, IIRC
Marc Gravell

Marc, masz absolutną rację. Celowo uniknąłem koncepcji „klasy błędu” (np. 5xx, 4xx, 3xx, 2xx itd.), Ponieważ otwiera to całą inną puszkę robaków. Nawet obsługa standardowych kodów (200, 302, 404, 500 itd.) Jest znacznie lepsza niż całkowite ignorowanie kodów.
David Taylor

1

Kontynuując już podane przykłady, powiedziałbym, że najlepszą praktyką jest zawinięcie odpowiedzi w taki sposób

    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;
        }
   }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.