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

发布于 2024-08-26 17:26:54 字数 2035 浏览 4 评论 0原文

当 .NET System.Uri 类解析字符串时,它会对输入执行一些规范化,例如将方案和主机名转为小写。它还会修剪每个路径段的尾随句点。后一个功能对于 OpenID 应用程序来说是致命的,因为某些 OpenID(例如从 Yahoo 发布的 OpenID)包含可能以句点结尾的 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 中等信任下工作(但不是绝对必要)。

When the .NET System.Uri class parses strings it performs some normalization on the input, such as lower-casing the scheme and hostname. It also trims trailing periods from each path segment. This latter feature is fatal to OpenID applications because some OpenIDs (like those issued from Yahoo) include base64 encoded path segments which may end with a period.

How can I disable this period-trimming behavior of the Uri class?

Registering my own scheme using UriParser.Register with a parser initialized with GenericUriParserOptions.DontCompressPath avoids the period trimming, and some other operations that are also undesirable for OpenID. But I cannot register a new parser for existing schemes like HTTP and HTTPS, which I must do for OpenIDs.

Another approach I tried was registering my own new scheme, and programming the custom parser to change the scheme back to the standard HTTP(s) schemes as part of parsing:

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

This actually almost works. The Uri instance reports https instead of httpsx everywhere -- except the Uri.Scheme property itself. That's a problem when you pass this Uri instance to the HttpWebRequest to send a request to this address. Apparently it checks the Scheme property and doesn't recognize it as 'https' because it just sends plaintext to the 443 port instead of SSL.

I'm happy for any solution that:

  1. Preserves trailing periods in path segments in Uri.Path
  2. Includes these periods in outgoing HTTP requests.
  3. Ideally works with under ASP.NET medium trust (but not absolutely necessary).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

画离情绘悲伤 2024-09-02 17:26:54

微软表示它将在 .NET 4.0 中修复(尽管从评论中看来它尚未修复)

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

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

感谢 jxdavis 和 Google 的回答:

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

Microsoft says it will be fixed in .NET 4.0 (though it appears from the comments that it has not been fixed yet)

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

There is a workaround on that page, however. It involves using reflection to change the options though, so it may not meet the medium trust requirement. Just scroll to the bottom and click on the "Workarounds" tab.

Thanks to jxdavis and Google for this answer:

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

执笏见 2024-09-02 17:26:54

我很好奇问题的一部分是否是您只考虑“不压缩路径”,而不是基本 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
    }
}

I'm curious if part of the problem is that you are only accounting for "don't compress path", instead of all the defaults of the base HTTP parser: (including 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);

That's as opposed to the news that has flags (for instance):

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

Dang, Brandon Black beat me to it while I was working on typing things up...

This may help with code readability:

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
    }
}
红尘作伴 2024-09-02 17:26:54

您应该能够预先转义“.”使用“%2E”,但这是廉价且肮脏的出路。

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

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

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

You should be able to precent escape the '.' using '%2E', but that's the cheap and dirty way out.

You might try playing around with the dontEscape option a bit and it may change how Uri is treating those characters.

More info here:
http://msdn.microsoft.com/en-us/library/system.uri.aspx

Also check out the following (see DontUnescapePathDotsAndSlashes):
http:// msdn.microsoft.com/en-us/library/system.genericuriparseroptions.aspx

对你的占有欲 2024-09-02 17:26:54

这有效吗?

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

Does this work?

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;
}}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文