C # Bagaimana cara memeriksa apakah URL ada / valid?

117

Saya membuat program sederhana dalam visual c # 2005 yang mencari simbol saham di Yahoo! Keuangan, unduh data historis, lalu plot riwayat harga untuk simbol ticker yang ditentukan.

Saya tahu URL persis yang saya perlukan untuk memperoleh data, dan jika pengguna memasukkan simbol ticker yang ada (atau setidaknya satu dengan data di Yahoo! Finance), itu berfungsi dengan baik. Namun, saya mengalami error run-time jika pengguna membuat simbol ticker, karena program mencoba menarik data dari halaman web yang tidak ada.

Saya menggunakan kelas WebClient, dan menggunakan fungsi DownloadString. Saya memeriksa semua fungsi anggota lain dari kelas WebClient, tetapi tidak melihat apa pun yang dapat saya gunakan untuk menguji URL.

Bagaimana saya bisa melakukan ini?

Daniel Waltrip
sumber
1
diperbarui untuk menampilkan penggunaan C # 2.0 (VS2005)
Marc Gravell

Jawaban:

110

Anda bisa mengeluarkan permintaan "HEAD" daripada "GET"?

(edit) - lol! Sepertinya saya pernah melakukan ini sebelumnya !; diubah ke wiki untuk menghindari tuduhan pengumpulan ulang. Jadi untuk menguji URL tanpa biaya mengunduh konten:

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

Anda akan try/ catchsekitar DownloadStringuntuk memeriksa kesalahan; tidak ada kesalahan? Itu ada ...


Dengan C # 2.0 (VS2005):

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

dan

using(WebClient client = new MyClient())
{
    // code as before
}
Marc Gravell
sumber
FWIW - Tidak yakin apakah itu benar-benar menyelesaikan masalah (selain mungkin sisi klien perilaku yang berbeda) karena Anda hanya mengubah metode HTTP. Respons dari server akan sangat bergantung pada bagaimana logika dikodekan dan mungkin tidak berfungsi dengan baik untuk layanan dinamis seperti harga saham. Untuk sumber daya statis (mis. Gambar, file, dll.) HEAD biasanya berfungsi seperti yang diiklankan sejak dimasukkan ke dalam server. Banyak programmer tidak secara eksplisit meminta HEAD karena fokusnya biasanya pada POST dan GET. YMMV
David Taylor
Maaf sudah terlalu lama memilih jawaban ... Saya teralihkan dengan sekolah dan pekerjaan dan agak lupa tentang posting ini. Sebagai sidenote, saya tidak bisa mendapatkan solusi Anda untuk bekerja karena saya menggunakan Visual Studio 2005 yang tidak memiliki tipe 'var'. Saya belum mengerjakan proyek ini selama berbulan-bulan, tetapi apakah ada perbaikan sederhana untuk fakta itu? Juga ketika saya mencoba untuk mengimplementasikan solusi Anda, saya ingat bahwa itu membuat saya marah karena mencoba mendefinisikan properti HeadOnly tanpa kode dalam definisi 'get' dan 'set'. Atau mungkin saya hanya melakukan sesuatu yang salah. Terima kasih atas bantuannya!
Daniel Waltrip
Apa itu MyClient ?
Kiquenet
@Kiquenet ada tautan di badan, ke sini: stackoverflow.com/questions/153451/…
Marc Gravell
136

Berikut adalah implementasi lain dari solusi ini:

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

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

BigJoe714
sumber
2
Saya menggunakan kode ini untuk memeriksa apakah ada banyak gambar, dan ini cukup lambat (beberapa detik per URL). Apakah seseorang tahu apakah ini masalah dengan kode ini, atau hanya fakta kehidupan saat melakukan panggilan semacam ini?
ssmith
@ssmith Salah satu cara untuk mempercepat kode Anda adalah dengan melakukan pemeriksaan dalam loop Parallel.Foreach jika Anda belum mencobanya. Itu membuat aplikasi pengujian url saya JAUH lebih cepat.
Jack Fairfield
3
Hal ini melempar DisposedObject sebagai gantinya (response.StatusCode == HttpStatusCode.OK); bungkus menggunakan
Lapenkov Vladimir
1
Ada masalah dengan kode di atas. jika Anda melakukan response.Close (); maka Anda tidak dapat memeriksa response.StatusCode karena itu akan memunculkan pengecualian.
lahir
@sith metode apapun lebih cepat?
Kiquenet
36

Solusi ini cukup bagus, tetapi mereka lupa bahwa mungkin ada kode status selain 200 OK. Ini adalah solusi yang saya gunakan di lingkungan produksi untuk pemantauan status dan semacamnya.

Jika ada pengalihan url atau beberapa kondisi lain pada halaman target, pengembaliannya akan benar menggunakan metode ini. Selain itu, GetResponse () akan mengeluarkan pengecualian dan karenanya Anda tidak akan mendapatkan Kode Status untuk itu. Anda perlu menjebak pengecualian dan memeriksa ProtocolError.

Kode status 400 atau 500 akan mengembalikan false. Semua yang lain membalas dengan benar. Kode ini mudah dimodifikasi agar sesuai dengan kebutuhan Anda akan kode status tertentu.

/// <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;
}
jsmith.dll
sumber
1
Saya akan menambahkan bahwa beberapa kode status dalam kisaran 3xx sebenarnya akan menyebabkan kesalahan dilempar misalnya 304 Tidak Dimodifikasi dalam hal ini Anda harus menanganinya di blok tangkapan Anda
RobV
3
Baru saja mengalami masalah tarik rambut Anda dengan pendekatan ini: HttpWebRequesttidak suka jika Anda tidak .Close()memiliki responseobjek sebelum Anda mencoba mengunduh yang lain. Butuh waktu berjam-jam untuk menemukan yang satu itu!
jbeldock
4
HttpWebResponseobjek harus diapit dalam usingblok karena diimplementasikan IDisposableyang juga akan memastikan penutupan koneksi. Ini mungkin menyebabkan masalah seperti yang dihadapi @jbeldock.
Habib
2
Itu melempar 404 Not Founds pada url yang berfungsi dengan baik di browser ...?
Michael Tranchida
@MichaelTranchida Web server terkenal 404 saat Anda mengeluarkan metode yang tidak didukung. Dalam kasus Anda Headmungkin tidak didukung pada sumber daya itu meskipun Getmungkin saja. Seharusnya malah melempar 405.
Sriram Sakthivel
9

Jika saya memahami pertanyaan Anda dengan benar, Anda dapat menggunakan metode kecil seperti ini untuk memberikan hasil tes URL Anda:

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;

Anda dapat membungkus kode di atas dalam sebuah metode dan menggunakannya untuk melakukan validasi. Saya harap ini menjawab pertanyaan yang Anda ajukan.

Software Kalender
sumber
1
Ya, mungkin Anda dapat memperbaiki solusi dengan membedakan kasus yang berbeda (kegagalan koneksi TCP - host menolak koneksi, 5xx - Terjadi sesuatu yang fatal, 404 - Sumber daya tidak ditemukan, dll). Lihat properti Status WebException;)
David Taylor
Poin yang sangat bagus David! Itu akan memberi kami umpan balik yang lebih detail sehingga kami dapat menangani kesalahan dengan lebih cerdik.
Perangkat Lunak Kalender
1
Terima kasih. Maksud saya adalah ada beberapa lapisan untuk bawang ini, yang masing-masing dapat memberikan kunci pas ke dalam pekerjaan (. Kerangka Kerja Bersih, Resolusi DNS, Konektivitas TCP, Server Web target, aplikasi target, dll.). IMHO desain yang baik harus dapat membedakan antara kondisi kegagalan yang berbeda untuk memberikan umpan balik yang informatif dan diagnostik yang dapat digunakan. Mari juga tidak lupa HTTP memiliki kode status karena suatu alasan;)
David Taylor
6

Coba ini (Pastikan Anda menggunakan System.Net):

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

Ketika fungsi checkWebsite () dipanggil, ia mencoba untuk mendapatkan kode sumber dari URL yang diteruskan ke dalamnya. Jika mendapat kode sumber, itu mengembalikan true. Jika tidak, itu mengembalikan salah.

Contoh Kode:

//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");
pengguna6909992
sumber
3

Ini opsi lain

public static bool UrlIsValid(string url)
{
    bool br = false;
    try {
        IPHostEntry ipHost = Dns.Resolve(url);
        br = true;
    }
    catch (SocketException se) {
        br = false;
    }
    return br;
}
Zain Ali
sumber
3
Itu mungkin berguna untuk memeriksa apakah sebuah host ada. Pertanyaannya jelas bukan tentang apakah tuan rumah itu ada atau tidak. Ini berkaitan dengan penanganan jalur HTTP yang buruk mengingat tuan rumah diketahui ada dan baik-baik saja .
binki
3

Solusi ini tampaknya mudah diikuti:

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 ;
}
abobjects.com
sumber
1
jangan lupa untuk menutup webResponse, jika tidak, waktu respons akan bertambah setiap kali Anda memanggil metode Anda
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");`
}
Praveen Dasare
sumber
1
Tolong tambahkan penjelasan untuk jawaban Anda. Jawaban kode saja cenderung membingungkan dan tidak membantu pembaca di masa mendatang dan dapat menarik suara negatif dengan cara itu.
Jesse
2

Saya memiliki cara yang lebih sederhana untuk menentukan apakah url valid.

if (Uri.IsWellFormedUriString(uriString, UriKind.RelativeOrAbsolute))
{
   //...
}
tsingroo
sumber
4
Tidak, metode ini tidak memeriksa apakah url benar-benar dapat diakses. Ia bahkan mengembalikan true ketika Uri.IsWellFormedUriString (" 192.168.1.421 ", ...), yang menggunakan url yang jelas-jelas salah
zhaorufei
2

Saya selalu menemukan bahwa Pengecualian jauh lebih lambat untuk ditangani.

Mungkin dengan cara yang kurang intensif kamu akan mendapatkan hasil yang lebih baik, lebih cepat,?

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

Kemudian gunakan saja:

IsValidUri(new Uri("http://www.google.com/censorship_algorithm"));
paku berkarat
sumber
1

Server web merespon dengan kode status HTTP yang menunjukkan hasil dari permintaan misalnya 200 (terkadang 202) berarti sukses, 404 - tidak ditemukan dll (lihat di sini ). Dengan asumsi bagian alamat server dari URL benar dan Anda tidak mendapatkan batas waktu soket, pengecualian kemungkinan besar memberi tahu Anda kode status HTTP selain 200. Saya akan menyarankan untuk memeriksa kelas pengecualian dan melihat apakah pengecualian tersebut membawa kode status HTTP.

IIRC - Panggilan yang dimaksud akan menampilkan WebException atau turunan. Periksa nama kelas untuk melihat yang mana dan gabungkan panggilan dalam blok percobaan untuk menjebak kondisi.

David Taylor
sumber
2
Sebenarnya, apa pun dalam kisaran 200-299 berarti sukses, IIRC
Marc Gravell
Marc, Anda benar sekali. Saya sengaja menghindari masuk ke konsep "kelas kesalahan" (misalnya 5xx, 4xx, 3xx, 2xx dll) karena itu akan membuka kaleng worm lainnya. Bahkan menangani kode standar (200, 302, 404, 500 dll) jauh lebih baik daripada mengabaikan kode sepenuhnya.
David Taylor
1

Mengikuti dari contoh yang sudah diberikan, saya akan mengatakan, praktik terbaik juga membungkus respons dalam penggunaan seperti ini

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