C#如何检查URL是否存在/有效?


117

我正在Visual C#2005中编写一个简单的程序,该程序在Yahoo!上查找股票代码。财务,下载历史数据,然后绘制指定股票代码的价格历史。

我知道我需要获取数据的确切URL,并且如果用户输入了一个现有的股票代号(或至少一个带有Yahoo! Finance上的数据的代号),它就可以很好地工作。但是,如果用户编写了股票代码,则会出现运行时错误,因为该程序试图从不存在的网页中提取数据。

我正在使用WebClient类,并使用DownloadString函数。我浏览了WebClient类的所有其他成员函数,但没有看到可用于测试URL的任何内容。

我怎样才能做到这一点?


1
更新以显示C#2.0(VS2005)的用法
Marc Gravell

Answers:


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被烘焙到服务器中,因此它通常按公告的方式工作。由于通常将重点放在POST和GET上,所以许多程序员没有明确地发送HEAD请求。YMMV
大卫·泰勒,

很抱歉花了这么长时间才找到答案...我在学校和工作上步履蹒跚,有点忘了这篇文章。附带说明,由于我使用的Visual Studio 2005没有'var'类型,因此我无法完全解决您的问题。我已经有几个月没有参与这个项目了,但是对于这个事实有简单的解决方法吗?同样,当我确实尝试实现您的解决方案时,我还记得它为试图定义HeadOnly属性而生气,而在“ get”和“ set”定义中没有任何代码。也许我只是做错了什么。谢谢您的帮助!
Daniel Waltrip

什么是MyClient
Kiquenet

@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.dotnetthoughts.net/2009/10/14/how-to-check-remote-file-exists-using-c/


2
我正在使用此代码来检查是否存在一堆图像,并且它非常慢(每个URL几秒钟)。有人知道这是此代码的问题,还是进行此类调用时的生活事实?
ssmith

@ssmith加快代码速度的一种方法是在Parallel.Foreach循环中进行检查(如果尚未尝试过的话)。它使我的url测试应用大大加快了速度。
杰克·费尔菲尔德

3
这些东西将DisposeObject作为回报抛出(response.StatusCode == HttpStatusCode.OK);包裹使用
Lapenkov Vladimir 18/12/27

1
上面的代码有问题。如果您做response.Close(); 那么您将无法检查response.StatusCode,因为它关闭时会抛出异常。
重生

@ssmith任何方法快得多吗?
Kiquenet

36

这些解决方案非常好,但是他们忘记了可能还有200 OK以外的其他状态代码。这是我在生产环境中用于状态监控等的解决方案。

如果目标页面上有url重定向或其他条件,则使用此方法将返回true。另外,GetResponse()将引发异常,因此您将不会获得其StatusCode。您需要捕获异常并检查是否存在ProtocolError。

任何400或500状态代码将返回false。所有其他返回true。可以轻松修改此代码,以满足您对特定状态代码的需求。

/// <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 Not Modified,在这种情况下,您应该在catch块中处理该错误
RobV

3
刚刚经历了拉你的头发出的问题,这种方法: HttpWebRequest如果你不并不像它.Close()response尝试下载任何东西之前对象。花了几个小时找到那个!
jbeldock

4
HttpWebResponse对象应该封闭在using块中,因为它实现了IDisposable,这也将确保关闭连接。这可能会导致@jbeldock遇到的问题。
哈比卜2013年

2
它会在浏览器上正常工作的网址上抛出404 Not Found。
Michael Tranchida

发出不支持的方法时,@ MichaelTranchida Web服务器以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的Status属性;)
David Taylor,

大卫很好!这将为我们提供更详细的反馈,以便我们可以更明智地处理错误。
Calendar Software

1
谢谢。我的观点是,该洋葱有多个层次,每个层次都可以使工作陷入僵局(.Net框架,DNS解析,TCP连接,目标Web服务器,目标应用程序等)。恕我直言,一个好的设计应该能够区分不同的故障情况,以提供有用的反馈和有用的诊断。让我们也不要忘记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的源代码。如果获取源代码,则返回true。如果不是,则返回false。

代码示例:

//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路径有关。
2015年

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

我有一种更简单的方法来确定天气是有效的。

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

4
不,此方法不会检查网址是否真正可访问。当Uri.IsWellFormedUriString(“ 192.168.1.421 ”,...)使用明显不正确的url 时,它甚至返回true
zhaorufei 2012年

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

Web服务器以HTTP状态代码进行响应,该HTTP状态代码指示请求的结果,例如200(有时为202)表示成功,404-未找到等(请参见此处)。假设URL的服务器地址部分正确并且您没有收到套接字超时,则该异常很可能告诉您HTTP状态码不是200。我建议检查该异常的类,并查看该异常是否携带HTTP状态代码。

IIRC-有问题的呼叫引发WebException或后代。检查类名称以查看哪个名称,并将调用包装在try块中以捕获条件。


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;
        }
   }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.