C# 如何创建使用GenericUriParserOptions.DontCompressPath解析的Uri实例

C# 如何创建使用GenericUriParserOptions.DontCompressPath解析的Uri实例,c#,.net,http,uri,C#,.net,Http,Uri,当.NETSystem.Uri类解析字符串时,它会对输入执行一些规范化,例如将方案和主机名的大小写降低。它还修剪每个路径段的尾随周期。后一个特性对OpenID应用程序来说是致命的,因为某些OpenID(如Yahoo发布的那些)包含base64编码的路径段,这些路径段可能以句点结束 如何禁用Uri类的此期间修剪行为? 使用UriParser注册我自己的方案。使用GenericUriParserOptions初始化的解析器注册。DontCompressPath避免了周期修剪,以及OpenID不需要的

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

如何禁用Uri类的此期间修剪行为?

使用
UriParser注册我自己的方案。使用
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

我很高兴任何解决方案:

  • Uri.path中的路径段中保留尾随句点
  • 在传出HTTP请求中包含这些句点
  • 理想情况下使用ASP.NET介质信任(但并非绝对必要)

  • 您应该能够使用“%2E”避免“.”,但这是一种既便宜又肮脏的方法

    您可以尝试稍微使用dontEscape选项,它可能会改变Uri处理这些角色的方式

    更多信息请点击此处:

    另请查看以下内容(请参见DonTuneScapePathDots和Slashes):
    http://msdn.microsoft.com/en-us/library/system.genericuriparseroptions.aspx

    微软表示它将在.NET 4.0中得到修复(尽管从评论中可以看出它还没有被修复)

    然而,在这一页上有一个解决办法。但是,它涉及使用反射来更改选项,因此可能无法满足中等信任要求。只需滚动到底部并单击“变通方法”选项卡

    感谢jxdavis和谷歌的回答:


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

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

    该死,布兰登·布莱克在我打字的时候打败了我

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

    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
        }
    }
    
    这行吗

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

    如果您的示例代码是失败的单元测试,则说明问题所在会更容易。单元测试必须设置HTTPS web服务器来证明失败:(你是否成功地解决了这个问题?你还需要帮助吗?是的,通过复杂的混合反射(当完全信任可用时)当完全信任不可用时,还有其他魔术。中的大部分代码都致力于玩所有必要的把戏,以获得99%的正确行为。对不起,应该在m_表上进行反思并删除现有条目。如果从
    GenericUriParser?这正是我添加上述注释的原因:-)我的意思是设置m_表而不是m_标志。不幸的是,MS Connect错误已过时。NET团队直接告诉我,.NET4.0不能修复dot bug。但解决办法很有趣。谢谢。死链接(connect.microsoft.com)。第二个链接指向页面底部的。谢谢,布兰登。
    DontUnescapePathDotsAndSlashes
    选项是一种可能的解决方法,但要有效地工作,它需要应用于现有的HTTP和HTTPS解析器,这仅在.NET 4.0中是可能的(除非您按照此处其他答案中的建议使用反射)。
    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
        }
    }
    
    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;
    }}