uri.Host如何抛出UriFormatException?

时间:2010-10-20 03:39:12

标签: c# .net exception url uri

foreach (var node in root.Find("a[href]"))
{
    var href = node.Attributes["href"].Value;
    Uri uri;
    try
    {
        uri = new Uri(item.Value.Uri, href);
    }
    catch(UriFormatException)
    {
        continue;
    }
    // *snip*
    try
    {
        if (_imageHosts.IsMatch(uri.Host)) // <--- problematic line
            priority--;
    }catch(UriFormatException)
    {
        MessageBox.Show(uri.OriginalString); // <--- gets displayed when I expected it wouldn't
        continue;
    }
    // *snip*
}

消息框显示的地址如

  

mailto:网站管理员[@] somehost?网站管理员

这显然是格式错误,但我没有得到的是为什么它没有被第一个 catch块捕获?

MSDN says它只能抛出InvalidOperationException。这很成问题,因为这意味着我的应用程序可以随时爆炸!

[[剪断]]

3 个答案:

答案 0 :(得分:8)

首先,我想说使用Exception来检查有效性并不是一个好主意,因为你可以使用Uri.TryCreate方法。因此,您可以重写代码,而不是依赖于可以抛出和捕获的异常。

更好地改变你的

Uri uri;
try
{
    uri = new Uri(item.Value.Uri, href);
}
catch(UriFormatException)
{
    continue;
}

Uri uri;
if (!Uri.TryCreate(item.Value.Uri, href, out uri)) continue;

但无论如何这还不是全面检查。

至于你的问题,答案相对简单。假设格式错误,你错了:

  

mailto:网站管理员[@] somehost?网站管理员

URI为Uniform Resource Identifier,因此其basic syntax

  

{scheme name}:{hierarchical part} [? {query}] [#{fragment}]

显然对您的输入有效。使用“mailto:”方案结束资源的URI。

当您尝试访问Host属性时,您认为资源是Http,但默认情况下使用的“mailto”-scheme解析器无法解析主机组件的原始字符串,因此引发了异常。

因此,要正确编写检查,您必须稍微修改一下代码:

Uri uri;
if (!Uri.TryCreate(item.Value.Uri, href, out uri)) continue;

if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps) continue;

阅读有关UriParser

的一些信息

此处根据@Mark评论进行更新。

  

当我试图获得AbsoluteUri属性时,我很确定它会引发异常..为什么会失败?

你不能通过Scheme检查,因为它将是“mailto”。所以这里快速测试:

        var baseUri = new Uri("http://localhost");
        const string href = "mailto: webmaster [ @ ] somehost ?webmaster";

        Uri uri;
        if (!Uri.TryCreate(baseUri,href, out uri)) 
        {
            Console.WriteLine("Can't create");
            return;
        }

        if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
        {
            Console.WriteLine("Wrong scheme");
            return;
        }

        Console.WriteLine("Testing uri: {0}", uri);

以“错误方案”结束。也许我不正确理解你?

将href更改为:

        const string href = "http: webmaster [ @ ] somehost ?webmaster";

它正确传递,自动转义为uri:

  

http://localhost/%20webmaster%20%5B%20@%20%5D%20somehost%20?webmaster

也可以使用所有uri的组件。

我在第一部分中尝试解释的主要问题是:

  

在我看来,您错误地将任何统一资源标识符视为基于http的网址,但这是错误的。 mailto:webmaster@somehost.tstgopher://gopher.hprc.utoronto.ca/myreshandler://something@somewhere也是有效的URI,可以成功解析。看看Official IANA-registered schemes

所以

  

Uri构造函数行为是预期且正确的。

它尝试验证known schemes的传入URI:

  • UriSchemeFile - 指定URI是指向文件的指针。
  • UriSchemeFtp - 指定通过文件传输协议(FTP)访问URI。
  • UriSchemeGopher - 指定通过Gopher协议访问URI。
  • UriSchemeHttp - 指定通过超文本传输​​协议(HTTP)访问URI
  • UriSchemeHttps - 指定通过安全超文本传输​​协议(HTTPS)访问URI。
  • UriSchemeMailto - 指定URI是电子邮件地址,可通过简单网络邮件协议(SNMP)访问。
  • UriSchemeNews - 指定URI是Internet新闻组,并通过网络新闻传输协议(NNTP)进行访问。
  • UriSchemeNntp - 指定URI是Internet新闻组,可通过网络新闻传输协议(NNTP)访问

当方案未知时使用基本URI解析器(参见URI scheme generic syntax)。


基本Uri.TryCreate()和方案检查足以获得可以传递给.NET HttpWebRequest的链接。你真的不需要检查它们是否格式正确或没有。如果链接不好(格式不正确或不存在),则在尝试请求时只会得到相应的HttpError。

至于你的例子:

  

http://www.google.com/search?q=cheesy poof

它通过我的支票并变为:

  

http://www.google.com/search?q=cheesy%20poof

您无需检查是否格式正确或没有。只需进行基本检查并尝试请求。希望它有所帮助。


  

此外,字符串mailto:webmaster [@] somehost?网站管理员格式不正确。我的字面意思是,那个字符串,愚蠢的[]和其中的一切

此字符串格式错误意思不是格式正确(因为包含排除在RFC 2396之后的字符)但它仍然可以被视为由于URI方案的一致性通用语法而有效(还要检查在使用http :)创建时如何转义。

答案 1 :(得分:1)

如果深入研究Uri.Host属性(真正的深层),它最终可以调用静态函数GetException,它会针对无效URI的不同条件返回UriFormatException个对象。打印出您获得的完整UriFormatException,并将其与Uri.GetException生成的{{1}}进行比较。您可能会从中获得更多详细信息。

答案 2 :(得分:1)

根据尼克的回答:

private static readonly string[] SupportedSchmes = { Uri.UriSchemeHttp, Uri.UriSchemeHttps, Uri.UriSchemeFtp, Uri.UriSchemeFile };

private static bool TryCreateUri(string uriString, out Uri result)
{
    return Uri.TryCreate(uriString, UriKind.Absolute, out result) && SupportedSchmes.Contains(result.Scheme);
}

private static bool TryCreateUri(Uri baseAddress, string relativeAddress, out Uri result)
{
    return Uri.TryCreate(baseAddress, relativeAddress, out result) && SupportedSchmes.Contains(result.Scheme);
}