【问题标题】:C# How can I check if a URL exists/is valid?C# 如何检查 URL 是否存在/是否有效?
【发布时间】:2010-10-29 19:24:31
【问题描述】:

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

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

我正在使用 WebClient 类,并使用 DownloadString 函数。我查看了 WebClient 类的所有其他成员函数,但没有看到可以用来测试 URL 的任何东西。

我该怎么做?

【问题讨论】:

  • 已更新以显示 C# 2.0 (VS2005) 用法

标签: c# .net url-validation


【解决方案1】:

您可以发出"HEAD" 请求而不是“GET”吗? 因此,无需下载内容即可测试 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");
}

您可以在DownloadString 周围使用try/catch 来检查错误;没有错误?它存在...


使用 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”类型。我已经好几个月没有从事这个项目了,但是对于这个事实有简单的解决方法吗?此外,当我确实尝试实施您的解决方案时,我记得它因为试图在“get”和“set”定义中没有代码来定义 HeadOnly 属性而生我的气。或者,也许我只是做错了什么。不过感谢您的帮助!
  • 什么是MyClient
  • @Kiquenet 在正文中有一个链接,到这里:stackoverflow.com/questions/153451/…
【解决方案2】:

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

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

【讨论】:

  • 实际上,200-299 范围内的任何东西都意味着成功,IIRC
  • 马克,你完全正确。我故意避免进入“错误类别”概念(例如 5xx、4xx、3xx、2xx 等),因为这会打开另一个蠕虫罐。即使处理标准代码(200、302、404、500 等)也比完全忽略代码要好得多。
【解决方案3】:

如果我正确理解您的问题,您可以使用这样的小方法为您提供 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;

您可以将上述代码包装在一个方法中并使用它来执行验证。我希望这能回答您提出的问题。

【讨论】:

  • 是的,也许您可​​以通过区分不同情况(TCP 连接失败 - 主机拒绝连接、5xx - 发生致命事件、404 - 找不到资源等)来优化解决方案。看看 WebException 的 Status 属性;)
  • 非常好,大卫!这将为我们提供更详细的反馈,以便我们能够更精明地处理错误。
  • 谢谢。我的观点是,这个洋葱有好几层,每一层都可以发挥作用(.Net 框架、DNS 解析、TCP 连接、目标 Web 服务器、目标应用程序等)。恕我直言,一个好的设计应该能够区分不同的故障情况,以提供信息反馈和可用的诊断。我们也不要忘记 HTTP 有状态码是有原因的 ;)
【解决方案4】:

这是此解决方案的另一种实现方式:

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/

【讨论】:

  • 我正在使用此代码检查是否存在一堆图像,而且速度很慢(每个 URL 几秒钟)。有人知道这是此代码的问题,还是只是进行此类调用时的现实情况?
  • @ssmith 加快代码速度的一种方法是在 Parallel.Foreach 循环中进行检查,如果您还没有尝试过的话。它使我的 url 测试应用程序更快。
  • 这个东西会抛出 DisposedObject 作为回报 (response.StatusCode == HttpStatusCode.OK);使用
  • 上面的代码有问题。如果你做 response.Close();那么你不能检查 response.StatusCode 因为它已经关闭它会抛出一个异常。
  • @ssmith 有更快的方法吗?
【解决方案5】:

这个解决方案似乎很容易理解:

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

【讨论】:

  • 别忘了关闭 webResponse,否则每次调用方法时响应时间都会增加
【解决方案6】:

这些解决方案很不错,但是他们忘记了可能还有其他状态代码而不是 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;
}

【讨论】:

  • 我要补充一点,3xx 范围内的一些状态码实际上会导致抛出错误,例如304 Not Modified 在这种情况下你应该在你的 catch 块中处理它
  • 刚刚经历了这种方法的拉扯你的头发的问题:HttpWebRequest 不喜欢它,如果你不尝试下载任何东西之前.Close() response 对象别的。花了几个小时才找到那个!
  • HttpWebResponse 对象应该包含在using 块中,因为它实现了IDisposable,这也将确保关闭连接。这可能会导致@jbeldock 面临的问题。
  • 它在浏览器中正常工作的 url 上抛出 404 Not Founds...?
  • 当您发出不受支持的方法时,@MichaelTranchida Web 服务器因 404 而臭名昭著。在您的情况下,Head 可能不支持该资源,尽管 Get 可能支持。它应该抛出 405。
【解决方案7】:

我有一种更简单的方法来确定网址是否有效。

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

【讨论】:

  • 不,这个方法不检查url是否真的可以访问。它甚至在 Uri.IsWellFormedUriString("192.168.1.421", ...) 使用明显不正确的 url 时返回 true
【解决方案8】:

这是另一种选择

public static bool UrlIsValid(string url)
{
    bool br = false;
    try {
        IPHostEntry ipHost = Dns.Resolve(url);
        br = true;
    }
    catch (SocketException se) {
        br = false;
    }
    return br;
}

【讨论】:

  • 这对于检查主机是否存在可能很有用。问题显然不是担心宿主是否存在。它关注处理错误的 HTTP 路径已知主机存在并且正常
【解决方案9】:

根据已经给出的示例,我想说,最好的做法是也将响应包装在这样的 using 中

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

【讨论】:

    【解决方案10】:

    试试这个(确保你使用 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");
    

    【讨论】:

      【解决方案11】:

      我一直发现异常处理起来要慢得多。

      也许一种不那么密集的方法会产生更好、更快的结果?

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

      【讨论】:

        【解决方案12】:
        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");`
        }
        

        【讨论】:

        • 请为您的答案添加一些解释。仅代码的答案往往会令人困惑,对未来的读者没有帮助,并且会以这种方式吸引反对票。
        【解决方案13】:

        很多答案都比 HttpClient 更早(我认为它是在 Visual Studio 2013 中引入的)或者没有 async/await 功能,所以我决定发布自己的解决方案:

        private static async Task<bool> DoesUrlExists(String url)
        {
            try
            {
                using (HttpClient client = new HttpClient())
                {
                    //Do only Head request to avoid download full file
                    var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));
        
                    if (response.IsSuccessStatusCode) {
                        //Url is available is we have a SuccessStatusCode
                        return true;
                    }
                    return false;
                }                
            } catch {
                    return false;
            }
        }
        

        我使用HttpClient.SendAsyncHttpMethod.Head 只发出一个头部请求,而不是下载整个文件。就像 David 和 Marc 已经说过不仅 http 200 可以,所以我使用 IsSuccessStatusCode 来允许所有成功状态代码。

        【讨论】:

          猜你喜欢
          • 2010-12-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-12
          • 2018-05-15
          • 2011-06-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多