C # Як я можу перевірити, чи існує URL / дійсна?


117

Я створюю просту програму у візуальній c # 2005, яка шукає символ запасу на Yahoo! Фінансування, завантаження історичних даних, а потім графік історії цін для зазначеного символу галочки.

Я знаю точну URL-адресу, яка мені потрібна для отримання даних, і якщо користувач вводить існуючий символ галочки (або хоча б один із даними на Yahoo! Finance), він працює чудово. Однак у мене є помилка під час виконання, якщо користувач створює символ-галочку, оскільки програма намагається витягнути дані з неіснуючої веб-сторінки.

Я використовую клас WebClient та використовую функцію DownloadString. Я переглянув усі інші функції класу WebClient, але не побачив нічого, що я міг би використати для перевірки URL-адреси.

Як я можу це зробити?


1
оновлено для показу C # 2.0 (VS2005) використання
Marc Gravell

Відповіді:


110

Ви могли б надіслати запит "HEAD", а не "GET"?

(редагувати) - lol! Схоже, я робив це раніше !; змінено на wiki, щоб уникнути звинувачень у повторному наборі. Тож для тестування URL-адреси без витрат на завантаження вмісту:

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

Ви б try/ catchнавколо DownloadStringперевіряли на наявність помилок; немає помилки? Він існує ...


З C # 2.0 (VS2005):

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

і

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

FWIW - Не впевнений, чи справді це вирішує проблему (крім, можливо, іншої поведінки клієнта), оскільки ви просто змінюєте метод HTTP. Відповідь сервера буде сильно залежати від того, як закодована логіка та може не працювати добре для динамічної послуги, як ціна акцій. Для статичних ресурсів (наприклад, зображень, файлів тощо) HEAD, як правило, працює так, як рекламується, оскільки він запечений на сервері. Багато програмістів не мають явних запитів HEAD, оскільки акцент зазвичай припадає на POST та GET. YMMV
Девід Тейлор

Вибачте за те, що зайняли так довго, щоб вибрати відповідь ... Мене змусили школу та роботу та свого роду забули про цю посаду. Як сторонне позначення, я не міг досить прийняти ваше рішення для роботи, оскільки я використовую Visual Studio 2005, який не має типу 'var'. Я не працював над цим проектом місяцями, але чи є просте виправлення цього факту? Крім того, коли я намагався реалізувати ваше рішення, я пам’ятаю, що на мене це розізлило спробу визначити властивість HeadOnly без коду у визначеннях «get» та «set». А може, я просто робив щось не так. Дякую за допомогу, хоча!
Даніель Уолтріп

Що таке MyClient ?
Кікенет

@Kiquenet є посилання в тілі, щоб тут: stackoverflow.com/questions/153451 / ...
Марк Gravell

136

Ось ще одна реалізація цього рішення:

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

Від: http://www.dotnetoughts.net/2009/10/14/how-to-check-remote-file-exists-using-c/


2
Я використовую цей код, щоб перевірити, чи існує купа зображень, і це досить повільно (пару секунд на URL). Хтось знає, чи це проблема з цим кодом чи просто факт життя під час здійснення таких дзвінків?
ssmith

@ssmith Один із способів прискорити свій код - це зробити перевірку в циклі Parallel.Foreach, якщо ви ще цього не пробували. Це зробило моє додаток для тестування URL набагато швидшим.
Джек Ферфілд

3
Цей матеріал кидає DisposedObject натомість (response.StatusCode == HttpStatusCode.OK); завершити використання
Лапенков Володимир

1
Існує проблема з вищевказаним кодом. якщо ви зробите відповідь.Закрийте (); тоді ви не можете перевірити відповідь.StatusCode, оскільки він близький, він викине виняток.
Відродження

@ssmith будь-який метод набагато швидше?
Кікенет

36

Ці рішення досить хороші, але вони забувають, що можуть бути інші коди статусу, ніж 200 ОК. Це рішення, яке я використовував у виробничих середовищах для моніторингу стану тощо.

Якщо на цільовій сторінці є переспрямування URL-адреси чи інша умова, повернення буде вірним за допомогою цього методу. Також GetResponse () викине виняток, отже, ви не отримаєте для нього StatusCode. Вам потрібно зафіксувати виняток і перевірити на наявність ProtocolError.

Будь-який код стану 400 або 500 поверне помилковим. Усі інші повертають правду. Цей код легко змінюється відповідно до ваших потреб у конкретних кодах статусу.

/// <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
Я додам, що деякі коди статусу в діапазоні 3xx насправді призведуть до помилки, наприклад, 304 Не змінено. У такому випадку вам слід обробляти це у вашому блоці лову
RobV

3
Тільки досвідчений навантажувальний ваш-волосся-аут проблеми з цим підходом: HttpWebRequestне подобається, якщо ви не .Close()на responseоб'єкт , перш ніж намагатися завантажити що - небудь ще. Знаходили години, щоб знайти це!
jbeldock

4
HttpWebResponseОб'єкт повинен бути укладений у usingблок, оскільки він реалізує, IDisposableщо також забезпечить закриття з'єднання. Це може спричинити проблеми, як зіткнувся @jbeldock.
Хабіб

2
Це метання 404 Not Founds для URL-адрес, які добре працюють у браузері ...?
Майкл Транчіда

Веб-сервери @MichaelTranchida відомі 404, коли ви видаєте метод, який не підтримується. У вашому випадку Headможливо не підтримується цей ресурс, хоча це Getможе бути. Натомість слід було кинути 405.
Шрірам Сактивель

9

Якщо я правильно розумію ваше запитання, ви можете скористатися таким маленьким методом, щоб дати вам результати свого тесту 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;

Ви можете загорнути вищевказаний код у спосіб і використовувати його для проведення перевірки. Я сподіваюся, що це відповідає на питання, яке ви задавали.


1
Так, можливо, ви можете вдосконалити рішення, розмежуючи різні випадки (збій TCP-з'єднання - хост відмовляється від з'єднання, 5xx - сталося щось фатальне, 404 - ресурс не знайдено тощо). Подивіться на властивість статусу WebException;)
Девід Тейлор

Дуже добре, Девіде! Це дасть нам більш детальний зворотний зв'язок, щоб ми могли впоратися з помилкою більш влучно.
Календарне програмне забезпечення

1
Дякую. Моя думка полягає в тому, що в цій цибулі є кілька шарів, кожен з яких може запустити гайковий ключ у твори (.Net Framework, DNS-роздільна здатність, TCP Connectivity, цільовий веб-сервер, цільовий додаток тощо). ІМХО хороша конструкція повинна мати можливість розмежовувати різні умови відмови для забезпечення інформативного зворотного зв’язку та корисної діагностики. Нехай також не забувають, що HTTP має причини статусу;)
Девід Тейлор,

6

Спробуйте це (переконайтеся, що ви використовуєте System.Net):

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

Коли функція checkWebsite () викликається, вона намагається отримати вихідний код URL-адреси, що передається в неї. Якщо він отримує вихідний код, він повертає true. Якщо ні, то він повертає помилковий.

Приклад коду:

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

Ось ще один варіант

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
Це може бути корисно для перевірки наявності хоста. Питання, очевидно, не хвилює, чи існує хост. Це стосується обробки неправильного шляху HTTP, якщо хост, як відомо, існує та добре .
бінкі

3

Це рішення виглядає легко:

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
не забудьте закрити webResponse, інакше час відповіді буде зростати щоразу, коли ви телефонуєте своєму методу
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
Будь ласка, додайте пояснення до своєї відповіді. Відповіді, що стосуються лише коду, зазвичай є заплутаними та не корисними для майбутніх читачів, і таким чином можуть залучати низові матеріали.
Джессі

2

У мене є більш простий спосіб визначити погоду, URL-адреса є дійсною.

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

4
Ні, цей метод не перевіряє, чи справді URL доступний. Він навіть повертає істину, коли Uri.IsWellFormedUriString (" 192.168.1.421 ", ...), які використовують явно неправильний URL
zhaorufei

2

Я завжди вважав, що з винятками набагато повільніше їх вирішувати.

Можливо, менш інтенсивний спосіб дозволить отримати кращий, швидший результат?

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

Тоді просто використовуйте:

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

1

Веб-сервери відповідають з кодом статусу HTTP, який вказує на результат запиту, наприклад, 200 (іноді 202) означає успіх, 404 - не знайдено тощо (див. Тут ). Якщо припустити, що частина адреси URL-адреси сервера є правильною, і ви не отримуєте час очікування сокета, виняток, швидше за все, повідомляє вам, що код статусу HTTP був іншим, ніж 200. Я б запропонував перевірити клас винятку і побачити, чи не містить цей виняток код статусу HTTP.

IIRC - Запитання, про яке йдеться, кидає WebException або нащадка. Перевірте ім'я класу, щоб побачити, який із них, і заверніть виклик у блок спробу, щоб утримати стан.


2
Насправді все, що є в діапазоні 200-299, означає успіх, IIRC
Marc Gravell

Марк, ти абсолютно прав. Я навмисно уникав потрапляння в концепцію "класу помилок" (наприклад, 5xx, 4xx, 3xx, 2xx тощо), оскільки це відкриває цілу іншу банку глистів. Навіть поводження зі стандартними кодами (200, 302, 404, 500 тощо) набагато краще, ніж повністю ігнорувати коди.
Девід Тейлор

1

Виходячи з уже наведених прикладів, я б сказав, що найкраща практика також відповідати результатом у подібному використанні

    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;
        }
   }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.