【问题标题】:Uri.TryCreate throws UriFormatException?Uri.TryCreate 抛出 UriFormatException?
【发布时间】:2009-07-13 15:46:30
【问题描述】:

我有一个尝试创建 Uri 然后清理它的方法(删除片段、排除某些域和查询字符串模式等)。该方法如下所示:

static public bool TryCreateCleanUri(Uri baseUri, string relstr, out Uri result)
{
    if (!Uri.TryCreate(baseUri, relstr, out result))
    {
        return false;
    }
    return CleanupUri(result, out result);
}

这种方法已经运行了好几个月。但昨晚它失败了。 Uri.TryCreate() 抛出异常!这是堆栈跟踪:

ERROR: Unhandled exception caught.  Program terminating.
System.UriFormatException: Invalid URI: The hostname could not be parsed.
   at System.Uri.CreateHostStringHelper(String str, UInt16 idx, UInt16 end, Flags& flags, String& scopeId)
   at System.Uri.CreateHostString()
   at System.Uri.GetComponentsHelper(UriComponents uriComponents, UriFormat uriFormat)
   at System.Uri.CombineUri(Uri basePart, String relativePart, UriFormat uriFormat)
   at System.Uri.GetCombinedString(Uri baseUri, String relativeStr, Boolean dontEscape, String& result)
   at System.Uri.ResolveHelper(Uri baseUri, Uri relativeUri, String& newUriString, Boolean& userEscaped, UriFormatException& e)
   at System.Uri.TryCreate(Uri baseUri, Uri relativeUri, Uri& result)
   at System.Uri.TryCreate(Uri baseUri, String relativeUri, Uri& result)

Uri.TryCreate(Uri, String, out Uri) 的文档说如果成功则返回值为True,否则返回值为False,但它对异常保持沉默。但是,Uri.TryCreate(Uri, Uri, out Uri) 的文档说:

此方法构造 URI,将 它以规范的形式,并验证 它。如果发生未处理的异常, 这个方法抓住了它。如果你想 创建一个 Uri 并获取异常使用 Uri 构造函数之一。

堆栈跟踪显示异常是在Uri.TryCreate(Uri, Uri, out Uri) 中引发的,根据文档,这不应该发生。

这是非常罕见的情况。我已经使用该代码几个月了,通过它运行了数十亿个 URL,直到现在还没有遇到问题。不幸的是,我不知道是什么组合导致了这个问题。我希望构建一个显示错误的测试用例。

这是Uri.TryCreate 中的已知错误,还是我遗漏了什么?

【问题讨论】:

  • 你知道它试图解析导致失败的 URL 吗?
  • 如果我知道,我会发布的。
  • 使用反射器找出“TryCreate”在做什么

标签: c# .net


【解决方案1】:

我不愿意等待几个月让我的代码再次遇到这种情况,我花了一些时间在 ILDASM 上弄清楚TryCreate 在做什么,然后再花一点时间想出一种重现错误的方法。

Uri.TryCreate(Uri baseUri, Uri relativeUri, out Uri result) 崩溃的原因似乎是格式错误的baseUri。例如,Uri 构造函数允许以下内容:

Uri badUri = new Uri("mailto:test1@mischel.comtest2@mischel.com");

根据 RFC for mailto: URI,这是不允许的。尽管构造函数创建并返回了一个Uri 对象,但尝试访问它的(某些)属性会抛出UriFormatException。例如,给定上面的代码,这行会抛出异常:

string badUriString = badUri.AbsoluteUri;

我觉得很有趣Uri 类似乎使用了两种不同的解析算法:一种在构造期间使用,另一种在内部用于获取各个组件。

将此无效的Uri 传递给TryCreate 将导致我在原始问题中描述的异常。 TryCreate 方法检查baseUri 参数是否为null,但不能(我想不能)验证它。它必须假设,如果参数不为 null,则传递的对象是完全初始化且有效的 Uri 实例。但是在构造结果的某个时刻,TryCreate 尝试获取baseUri 的组件并抛出异常。

我不能说我的程序实际上遇到了这样格式化的 mailto: URL。不过,我可以肯定地说,无效的Uri 对象是我程序崩溃的原因,这仅仅是因为我程序的异常堆栈跟踪与测试程序的堆栈跟踪匹配。简单地说,这个错误在Uri 构造函数中(以及在TryCreate 方法中),它允许创建无效的Uri

您可以在 Microsoft Connect 上关注bug report

【讨论】:

  • Connect 已关闭,解决方法是什么?我是否需要担心.net-4.6+ 上的延迟错误检测,或者我可以假设new Uri(someString) 不抛出足以表明someString 是根据System.Uri 的有效URI?
【解决方案2】:

既然您知道它可能会失败,让我们获取更多信息:

static public bool TryCreateCleanUri(Uri baseUri, string relstr, out Uri result)
{
    try {
        if (!Uri.TryCreate(baseUri, relstr, out result))
        {
            return false;
        }
    }
    catch (UriFormatException ex) {
        throw new InvalidOperationException(
            String.Format("Can create URI for base={0}, rel={1}", baseUri.ToString(), relstr),
            ex);
    }        
    return CleanupUri(result, out result);
}

【讨论】:

  • 我在我的应用程序中添加了类似的代码。不幸的是,在错误被触发之前可能需要很多天(几周或几个月)。我正在尝试构建一个测试用例。
  • 好的。请注意,重要的部分是捕获并显示 TryCreate 的两个输入参数。我敢打赌,当您看到答案时,答案会跳出来,尤其是因为您还将拥有堆栈跟踪。
【解决方案3】:
 public static bool CheckUrlValid(string url)
    {
        Uri uriResult;
        bool result = Uri.TryCreate(url, UriKind.Absolute, out uriResult);
        if(result)
        {
            uriResult = new Uri(url);
            if (uriResult.Scheme == Uri.UriSchemeHttps || uriResult.Scheme == Uri.UriSchemeHttp)
                return true;
        }

        return false;
    }

【讨论】:

  • 这对我来说似乎很好用。如果你尝试使用 Uri 变量而不初始化它,会导致错误。
  • 欢迎来到 StackOverflow!发布问题的答案时,请发布的不仅仅是您的代码。解释它,展示它的用法,做任何事情来使你的答案尽可能清晰,但答案应该永远不仅仅是代码。谢谢,祝你好运!
  • 感谢您的想法,但已经晚了大约七年,并没有真正解决这个问题。你看,我的意思是Uri.TryCreate 抛出了一个它不应该抛出的异常。您提出的解决方案无济于事。我最终使用类似于 John Saunders 在他的回答中提出的代码确定了问题。即捕获异常并输出触发错误的URL。
  • 您的代码第二次不必要地分配给uriResult,并且与问题无关。您可能想删除您的答案以挽救您的代表。
猜你喜欢
  • 1970-01-01
  • 2016-12-10
  • 1970-01-01
  • 2018-12-02
  • 1970-01-01
  • 2014-01-29
  • 1970-01-01
  • 2011-05-08
  • 1970-01-01
相关资源
最近更新 更多