C # ฉันจะตรวจสอบได้อย่างไรว่ามี URL อยู่ / ถูกต้องหรือไม่?


117

ฉันกำลังสร้างโปรแกรมง่ายๆใน visual c # 2005 ที่ค้นหาสัญลักษณ์หุ้นบน Yahoo! การเงินดาวน์โหลดข้อมูลประวัติจากนั้นลงจุดประวัติราคาสำหรับสัญลักษณ์สัญลักษณ์ที่ระบุ

ฉันทราบ URL ที่แน่นอนที่ต้องใช้ในการรับข้อมูลและหากผู้ใช้ป้อนชื่อย่อหุ้นที่มีอยู่ (หรืออย่างน้อยหนึ่งรายการพร้อมข้อมูลบน Yahoo! Finance) ก็จะทำงานได้ดีอย่างสมบูรณ์ อย่างไรก็ตามฉันมีข้อผิดพลาดรันไทม์หากผู้ใช้สร้างสัญลักษณ์แทนเนื่องจากโปรแกรมพยายามดึงข้อมูลจากหน้าเว็บที่ไม่มีอยู่จริง

ฉันกำลังใช้คลาส WebClient และใช้ฟังก์ชัน DownloadString ฉันดูฟังก์ชันสมาชิกอื่น ๆ ทั้งหมดของคลาส WebClient แต่ไม่เห็นสิ่งใดที่ฉันสามารถใช้ทดสอบ URL ได้

ฉันจะทำเช่นนี้ได้อย่างไร?


1
อัปเดตเพื่อแสดงการใช้งาน C # 2.0 (VS2005)
Marc Gravell

คำตอบ:


110

คุณสามารถส่งคำขอ"HEAD"แทน "GET" ได้หรือไม่

(แก้ไข) - ฮ่า ๆ ! ดูเหมือนว่าฉันเคยทำมาแล้ว !; เปลี่ยนเป็น 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
David Taylor

ขออภัยที่ใช้เวลานานมากในการเลือกคำตอบ ... ฉันเข้าข้างโรงเรียนและงานและลืมโพสต์นี้ไปแล้ว ในฐานะที่เป็นเครื่องมือช่วยแก้ปัญหาให้คุณใช้งานไม่ได้เพราะฉันใช้ Visual Studio 2005 ซึ่งไม่มีประเภท 'var' ฉันไม่ได้ทำงานในโครงการนี้มาหลายเดือนแล้ว แต่มีวิธีแก้ไขง่ายๆสำหรับข้อเท็จจริงนั้นหรือไม่? นอกจากนี้เมื่อฉันพยายามใช้โซลูชันของคุณฉันจำได้ว่าฉันโกรธมากที่พยายามกำหนดคุณสมบัติ HeadOnly โดยไม่มีรหัสในคำจำกัดความ 'get' และ 'set' หรือบางทีฉันแค่ทำอะไรผิด ขอบคุณสำหรับความช่วยเหลือ!
Daniel Waltrip

MyClientคืออะไร?
Kiquenet

@Kiquenet มีลิงค์ในเนื้อหามาที่นี่: stackoverflow.com/questions/153451/…
Marc 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.dotnetthoughts.net/2009/10/14/how-to-check-remote-file-exists-using-c/


2
ฉันใช้รหัสนี้เพื่อตรวจสอบว่ามีรูปภาพจำนวนมากหรือไม่และค่อนข้างช้า (สองสามวินาทีต่อ URL) มีใครรู้บ้างว่านี่เป็นปัญหากับรหัสนี้หรือเป็นเพียงความจริงของชีวิตเมื่อทำการโทรประเภทนี้
ssmith

@ssmith วิธีหนึ่งที่คุณสามารถเร่งความเร็วโค้ดของคุณได้คือทำการตรวจสอบแบบขนานวงต่างประเทศหากคุณยังไม่ได้ลอง มันทำให้แอปทดสอบ url ของฉันเร็วขึ้นมาก
Jack Fairfield

3
สิ่งนี้จะพ่นทิ้ง DisposedObject ในทางกลับกัน (response.StatusCode == HttpStatusCode.OK); ห่อโดยใช้
Lapenkov Vladimir

1
มีปัญหากับโค้ดด้านบน ถ้าคุณตอบสนองปิด (); จากนั้นคุณจะไม่สามารถตรวจสอบการตอบสนองได้สถานะรหัสเมื่อใกล้จะเกิดข้อยกเว้น
เกิดใหม่

@ssmith วิธีไหนเร็วกว่ากัน?
Kiquenet

36

วิธีแก้ปัญหาเหล่านี้ค่อนข้างดี แต่พวกเขาลืมไปว่าอาจมีรหัสสถานะอื่นมากกว่า 200 OK นี่เป็นโซลูชันที่ฉันใช้กับสภาพแวดล้อมการผลิตสำหรับการตรวจสอบสถานะและอื่น ๆ

หากมีการเปลี่ยนเส้นทาง 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 ประสบ
Habib

2
กำลังขว้าง 404 Not Founds ใน url ที่ทำงานได้ดีในเบราว์เซอร์ ... ?
Michael Tranchida

@MichaelTranchida เว็บเซิร์ฟเวอร์ขึ้นชื่อเรื่อง 404 เมื่อคุณใช้วิธีการที่ไม่รองรับ ในกรณีของคุณHeadอาจไม่ได้รับการสนับสนุนทรัพยากรนั้นแม้ว่าGetอาจจะเป็น มันควรจะโยน 405 แทน
Sriram Sakthivel

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;)
David Taylor

ดีมากเดวิด! นั่นจะทำให้เราได้รับคำติชมโดยละเอียดเพื่อให้เราจัดการกับข้อผิดพลาดได้อย่างชาญฉลาดยิ่งขึ้น
ซอฟต์แวร์ปฏิทิน

1
ขอบคุณ ประเด็นของฉันคือมีหลายเลเยอร์สำหรับหัวหอมนี้ซึ่งแต่ละอันสามารถโยนประแจเข้าไปในผลงาน (.Net Framework, DNS Resolution, การเชื่อมต่อ TCP, เว็บเซิร์ฟเวอร์เป้าหมาย, แอปพลิเคชันเป้าหมาย ฯลฯ ) IMHO การออกแบบที่ดีควรสามารถแยกแยะระหว่างเงื่อนไขความล้มเหลวต่างๆเพื่อให้ข้อมูลย้อนกลับและการวินิจฉัยที่ใช้งานได้ อย่าลืมว่า HTTP มีรหัสสถานะด้วยเหตุผล)
David Taylor

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 ที่ส่งผ่านเข้าไป หากได้รับซอร์สโค้ดจะส่งคืนจริง ถ้าไม่มันจะส่งกลับเท็จ

ตัวอย่างรหัส:

//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 ที่ไม่ดีให้เป็นเจ้าภาพเป็นที่รู้จักกันอยู่และจะปรับ
binki

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
โปรดเพิ่มคำอธิบายในคำตอบของคุณ คำตอบแบบใช้รหัสเท่านั้นมักจะสร้างความสับสนและไม่เป็นประโยชน์ต่อผู้อ่านในอนาคตและสามารถดึงดูดการโหวตด้วยวิธีนั้นได้
Jesse

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

มาร์คคุณถูกต้อง ฉันตั้งใจหลีกเลี่ยงการเข้าสู่แนวคิด "class of error" (เช่น 5xx, 4xx, 3xx, 2xx เป็นต้น) เนื่องจากเป็นการเปิดเวิร์มกระป๋องอื่น ๆ ทั้งหมด แม้แต่การจัดการรหัสมาตรฐาน (200, 302, 404, 500 ฯลฯ ) ก็ดีกว่าการละเว้นรหัสโดยสิ้นเชิง
David Taylor

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;
        }
   }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.