【问题标题】:How to create a Uri instance parsed with GenericUriParserOptions.DontCompressPath如何创建使用 GenericUriParserOptions.DontCompressPath 解析的 Uri 实例
【发布时间】:2011-01-31 17:43:19
【问题描述】:

当 .NET System.Uri 类解析字符串时,它会对输入执行一些规范化,例如将方案和主机名小写。它还修剪每个路径段的尾随周期。后一种特性对 OpenID 应用程序来说是致命的,因为一些 OpenID(例如从 Yahoo 发布的那些)包含可能以句点结尾的 base64 编码路径段。

如何禁用 Uri 类的这种周期修剪行为?

使用UriParser.Register 注册我自己的方案并使用GenericUriParserOptions.DontCompressPath 初始化的解析器避免了句点修整以及其他一些对于OpenID 来说也是不可取的操作。但是我无法为现有的方案(如 HTTP 和 HTTPS)注册新的解析器,而我必须为 OpenID 做这些。

我尝试的另一种方法是注册我自己的新方案,并编写自定义解析器以将方案更改回标准 HTTP(s) 方案作为解析的一部分:

public class MyUriParser : GenericUriParser
{
    private string actualScheme;

    public MyUriParser(string actualScheme)
        : base(GenericUriParserOptions.DontCompressPath)
    {
        this.actualScheme = actualScheme.ToLowerInvariant();
    }

    protected override string GetComponents(Uri uri, UriComponents components, UriFormat format)
    {
        string result = base.GetComponents(uri, components, format);

        // Substitute our actual desired scheme in the string if it's in there.
        if ((components & UriComponents.Scheme) != 0)
        {
            string registeredScheme = base.GetComponents(uri, UriComponents.Scheme, format);
            result = this.actualScheme + result.Substring(registeredScheme.Length);
        }

        return result;
    }
}

class Program
{
    static void Main(string[] args)
    {
        UriParser.Register(new MyUriParser("http"), "httpx", 80);
        UriParser.Register(new MyUriParser("https"), "httpsx", 443);
        Uri z = new Uri("httpsx://me.yahoo.com/b./c.#adf");
        var req = (HttpWebRequest)WebRequest.Create(z);
        req.GetResponse();
    }
}

这实际上几乎有效。 Uri 实例到处报告 https 而不是 httpsx —— 除了 Uri.Scheme 属性本身。当您将此Uri 实例传递给HttpWebRequest 以向该地址发送请求时,这是一个问题。显然它检查了 Scheme 属性并且不将其识别为“https”,因为它只是将纯文本发送到 443 端口而不是 SSL。

我很高兴有任何解决方案:

  1. 在Uri.Path 的路径段中保留尾随句点
  2. 在传出的 HTTP 请求中包含这些句点。
  3. 在 ASP.NET 中等信任下理想地工作(但不是绝对必要的)。

【问题讨论】:

  • 如果您的示例代码是一个失败的单元测试来说明问题所在会更容易。
  • 单元测试必须设置一个 HTTPS Web 服务器来证明失败。 :(
  • 你有没有成功解决过这个问题?您还需要这方面的帮助吗?
  • 是的,当完全信任不可用时,通过反射(当完全信任可用时)和其他魔法的复杂混合。 github.com/AArnott/dotnetopenid/blob/v3.4/src/DotNetOpenAuth/… 中的大部分代码都致力于发挥所有必要的技巧来获得 99% 的正确行为。

标签: c# .net http uri


【解决方案1】:

您应该可以直接转义 '.'使用 '%2E',但这是廉价而肮脏的出路。

您可以尝试使用 dontEscape 选项,它可能会改变 Uri 对待这些角色的方式。

更多信息在这里: http://msdn.microsoft.com/en-us/library/system.uri.aspx

还请查看以下内容(请参阅 DontUnescapePathDotsAndSlashes): http://msdn.microsoft.com/en-us/library/system.genericuriparseroptions.aspx

【讨论】:

  • 谢谢,布兰登。 DontUnescapePathDotsAndSlashes 选项是一种可能的解决方法,尽管要有效地工作,它需要应用于现有的 HTTP 和 HTTPS 解析器,这只能在 .NET 4.0 中实现(除非您使用反射,如此处其他答案中所建议的那样)。
【解决方案2】:

Microsoft 表示它将在 .NET 4.0 中修复(尽管从 cmets 看来它尚未修复)

https://connect.microsoft.com/VisualStudio/feedback/details/386695/system-uri-incorrectly-strips-trailing-dots?wa=wsignin1.0#tabs

但是,该页面上有一个解决方法。它涉及使用反射来更改选项,因此它可能不符合中等信任要求。只需滚动到底部并单击“解决方法”选项卡即可。

感谢 jxdavis 和 Google 的回答:

http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/5206beca-071f-485d-a2bd-657d635239c9

【讨论】:

  • 不幸的是,MS Connect 错误已过时。 .NET 团队直接告诉我,.NET 4.0 没有修复 dot 错误。但解决方法很有趣。谢谢。
  • 死链接 (connect.microsoft.com)。第二个链接指向页面底部的stackoverflow.com/a/2285321/1462295。
【解决方案3】:

我很好奇问题的一部分是否在于您只考虑“不压缩路径”,而不是基本 HTTP 解析器的所有默认值:(包括 UnEscapeDotsAndSlashes)

  private const UriSyntaxFlags HttpSyntaxFlags = (UriSyntaxFlags.AllowIriParsing | UriSyntaxFlags.AllowIdn | UriSyntaxFlags.UnEscapeDotsAndSlashes | UriSyntaxFlags.CanonicalizeAsFilePath | UriSyntaxFlags.CompressPath | UriSyntaxFlags.ConvertPathSlashes | UriSyntaxFlags.PathIsRooted | UriSyntaxFlags.AllowAnInternetHost | UriSyntaxFlags.AllowUncHost | UriSyntaxFlags.MayHaveFragment | UriSyntaxFlags.MayHaveQuery | UriSyntaxFlags.MayHavePath | UriSyntaxFlags.MayHavePort | UriSyntaxFlags.MayHaveUserInfo | UriSyntaxFlags.MustHaveAuthority);

这与带有标志的新闻相反(例如):

 private const UriSyntaxFlags NewsSyntaxFlags = (UriSyntaxFlags.AllowIriParsing | UriSyntaxFlags.MayHaveFragment | UriSyntaxFlags.MayHavePath);

当我正在打字的时候,Brandon Black 打败了我……

这可能有助于提高代码的可读性:

namespace System 
{
    [Flags]
    internal enum UriSyntaxFlags
    {
        AllowAnInternetHost = 0xe00,
        AllowAnyOtherHost = 0x1000,
        AllowDnsHost = 0x200,
        AllowDOSPath = 0x100000,
        AllowEmptyHost = 0x80,
        AllowIdn = 0x4000000,
        AllowIPv4Host = 0x400,
        AllowIPv6Host = 0x800,
        AllowIriParsing = 0x10000000,
        AllowUncHost = 0x100,
        BuiltInSyntax = 0x40000,
        CanonicalizeAsFilePath = 0x1000000,
        CompressPath = 0x800000,
        ConvertPathSlashes = 0x400000,
        FileLikeUri = 0x2000,
        MailToLikeUri = 0x4000,
        MayHaveFragment = 0x40,
        MayHavePath = 0x10,
        MayHavePort = 8,
        MayHaveQuery = 0x20,
        MayHaveUserInfo = 4,
        MustHaveAuthority = 1,
        OptionalAuthority = 2,
        ParserSchemeOnly = 0x80000,
        PathIsRooted = 0x200000,
        SimpleUserSyntax = 0x20000,
        UnEscapeDotsAndSlashes = 0x2000000,
        V1_UnknownUri = 0x10000
    }
}

【讨论】:

    【解决方案4】:

    这行得通吗?

    public class MyUriParser : UriParser
    {
    private string actualScheme;
    
    public MyUriParser(string actualScheme)
    {
        Type type = this.GetType();
        FieldInfo fInfo = type.BaseType.GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
        fInfo.SetValue(this, GenericUriParserOptions.DontCompressPath);
        this.actualScheme = actualScheme.ToLowerInvariant();
    }
    
    protected override string GetComponents(Uri uri, UriComponents components, UriFormat format)
    {
        string result = base.GetComponents(uri, components, format);
    
        // Substitute our actual desired scheme in the string if it's in there. 
        if ((components & UriComponents.Scheme) != 0)
        {
            string registeredScheme = base.GetComponents(uri, UriComponents.Scheme, format);
            result = this.actualScheme + result.Substring(registeredScheme.Length);
        }
    
        return result;
    }}
    

    【讨论】:

    • 抱歉,应该反映 m_Table 并删除现有条目。
    • 如果你派生自GenericUriParser,为什么要使用反射来设置一个可以在基本构造函数中轻松设置的标志?
    • 这正是我在上面添加评论的原因 :-) 我的意思是设置 m_Table 而不是 m_Flags。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 2013-07-18
    • 2023-03-27
    • 2016-10-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多