我相信 Java的URI.resolve方法的定义和实现与 RFC 3986 section 5.2.2不兼容.我知道Java API定义了该方法的工作原理,如果现在改变它会破坏现有的应用程序,但我的问题是:任何人都可以确认我的理解是这种方法与RFC 3986不兼容?

我正在使用这个问题的例子:java.net.URI resolve against only query string,我将在这里复制:

我正在使用JDK java.net.URI来构建URI.
我想附加到一个绝对的URI对象,一个查询(在String中).例如:

URI base = new URI("http://example.com/something/more/long");
String queryString = "query=http://local:282/rand&action=aaaa";
URI query = new URI(null,null,queryString,null);
URI result = base.resolve(query);

理论(或我认为的)是这样的决心应该返回:

http://example.com/something/more/long?query=http://local:282/rand&action=aaaa

但是我得到的是:

http://example.com/something/more/?query=http://local:282/rand&action=aaaa

我对RFC 3986 section 5.2.2的理解是,如果相对URI的路径为空,则使用基本URI的整个路径:

if (R.path == "") then
           T.path = Base.path;
           if defined(R.query) then
              T.query = R.query;
           else
              T.query = Base.query;
           endif;

并且只有指定路径是要与基本路径合并的相对路径:

else
           if (R.path starts-with "/") then
              T.path = remove_dot_segments(R.path);
           else
              T.path = merge(Base.path,R.path);
              T.path = remove_dot_segments(T.path);
           endif;
           T.query = R.query;
        endif;

但Java实现始终执行合并,即使路径为空:

String cp = (child.path == null) ? "" : child.path;
    if ((cp.length() > 0) && (cp.charat(0) == '/')) {
      // 5.2 (5): Child path is absolute
      ru.path = child.path;
    } else {
      // 5.2 (6): Resolve relative path
      ru.path = resolvePath(base.path,cp,base.isAbsolute());
    }

如果我的阅读是正确的,要从RFC伪代码获取此行为,您可以在查询字符串之前将一个点作为相对URI中的路径,从我使用相对URI作为网页链接的经验来看,是我期望的:

transform(Base="http://example.com/something/more/long",R=".?query")
    => T="http://example.com/something/more/?query"

但是我会期望在一个网页上,页面“http://example.com/something/more/long”到“查询”的链接将转到“http://example.com/something/more/long?query”,而不是“http://example.com/something/more/?query” – 换句话说,与RFC一致,但不是与Java实现.

我的阅读RFC是否正确,Java方法与它不一致,还是我错过了什么?

解决方法

是的,我同意URI.resolve(URI)方法与RFC 3986不兼容.原来的问题本身就提供了大量的研究,有助于这个结论.首先,让我们清除任何混乱.

如Raedwald所解释的(在现在删除的答案中),以/结尾或不以/结尾的基本路径有区别:

> fizz相对于/ foo / bar是:/ foo / fizz
> fizz相对于/ foo / bar /是:/ foo / bar / fizz

虽然正确,但这不是一个完整的答案,因为原来的问题不是询问一个path(即上面的“fizz”).相反,该问题涉及相对URI引用的单独的query component. URI类constructor used in the example code接受五个不同的String参数,除了queryString参数之外的所有参数都被传递为空. (请注意,Java接受一个空字符串作为路径参数,这在逻辑上会导致一个“空”路径组件,因为“the path component is never undefined”虽然它是“may be empty (zero length)”.)这将在以后很重要.

在earlier comment年,Sajan Chandran指出,java.net.URI class被记录为实现RFC 2396,而不是RFC 3986的问题.前者在2005年被后者废弃了.URI类Javadoc没有提到新的RFC可以被解释作为其不兼容的更多证据.我们再多一点:

> JDK-6791060是一个开放的问题,建议这个类“应该更新为RFC 3986”.那里的一个评论警告说,“RFC3986并没有完全倒退
兼容2396“.
>以前尝试将URI类的部分更新为符合RFC 3986(例如JDK-6348622),但是后来的兼容性为rolled back. (另见JDK邮件列表中的this discussion)
>尽管路径“合并”逻辑听起来相似,如noted by SubOptimal所示,较新的RFC中指定的伪代码与actual implementation不匹配.在伪代码中,当相对URI的路径为空时,生成的目标路径按原样复制基本URI.在这些条件下不执行“合并”逻辑.与该规范相反,Java的URI实现修剪了最后一个字符之后的基本路径,如问题所述.

如果您想要RFC 3986行为,则可以使用URI类的替代方法. Java EE 6实现提供javax.ws.rs.core.UriBuilder,(在泽西1.18)似乎按照预期的行为(见下文).至少就RFC的编码而言,就编码不同URI组件而言至关重要.

在J2EE之外,Spring 3.0引入了UriUtils,具体记录了“基于RFC 3986的编码和解码”. Spring 3.1不推荐使用其中的一些功能,并引入了UriComponentsBuilder,但是它不符合任何特定的RFC,不幸的是.

测试程序,展示不同的行为:

import java.net.*;
import java.util.*;
import java.util.function.*;
import javax.ws.rs.core.UriBuilder; // using Jersey 1.18

public class StackOverflow22203111 {

    private URI withResolveURI(URI base,String targetQuery) {
        URI reference = queryOnlyURI(targetQuery);
        return base.resolve(reference);
    }

    private URI withUriBuilderReplaceQuery(URI base,String targetQuery) {
        UriBuilder builder = UriBuilder.fromUri(base);
        return builder.replaceQuery(targetQuery).build();
    }

    private URI withUriBuilderMergeURI(URI base,String targetQuery) {
        URI reference = queryOnlyURI(targetQuery);
        UriBuilder builder = UriBuilder.fromUri(base);
        return builder.uri(reference).build();
    }

    public static void main(String... args) throws Exception {

        final URI base = new URI("http://example.com/something/more/long");
        final String queryString = "query=http://local:282/rand&action=aaaa";
        final String expected =
            "http://example.com/something/more/long?query=http://local:282/rand&action=aaaa";

        StackOverflow22203111 test = new StackOverflow22203111();
        Map<String,BiFunction<URI,String,URI>> strategies = new LinkedHashMap<>();
        strategies.put("URI.resolve(URI)",test::withResolveURI);
        strategies.put("UriBuilder.replaceQuery(String)",test::withUriBuilderReplaceQuery);
        strategies.put("UriBuilder.uri(URI)",test::withUriBuilderMergeURI);

        strategies.forEach((name,method) -> {
            System.out.println(name);
            URI result = method.apply(base,queryString);
            if (expected.equals(result.toString())) {
                System.out.println("   MATCHES: " + result);
            }
            else {
                System.out.println("  EXPECTED: " + expected);
                System.out.println("   but WAS: " + result);
            }
        });
    }

    private URI queryOnlyURI(String queryString)
    {
        try {
            String scheme = null;
            String authority = null;
            String path = null;
            String fragment = null;
            return new URI(scheme,authority,path,fragment);
        }
        catch (URISyntaxException SyntaxError) {
            throw new IllegalStateException("unexpected",SyntaxError);
        }
    }
}

输出:

URI.resolve(URI)
  EXPECTED: http://example.com/something/more/long?query=http://local:282/rand&action=aaaa
   but WAS: http://example.com/something/more/?query=http://local:282/rand&action=aaaa
UriBuilder.replaceQuery(String)
   MATCHES: http://example.com/something/more/long?query=http://local:282/rand&action=aaaa
UriBuilder.uri(URI)
   MATCHES: http://example.com/something/more/long?query=http://local:282/rand&action=aaaa

当相对URI包含空路径时,Java的URI.resolve与RFC 3986不兼容吗?的更多相关文章

  1. 使用xib创建自定义视图

    但是,您必须接受您的XIB将包含根视图或其他内容,这些视图将作为子视图添加到放入Placement的类的实例中.这样,你应该有类似的东西:XIB与您的自定义视图内容:添加XIB的位置:由于添加到展示位置的视图实例与XIB中的文件所有者相同,因此您可以在XIB和Placement中设置出口和操作.只是不要忘记你的XIB中的根视图不是UIKit将创建放置到Placement的实例.为方便起见,请在下面找到我的代码,该代码是基类,以便于创建此类视图:

  2. ios – 使用简洁的NSManagedObjectID URI形式?

    我想避免字符串操作,因为感觉到icky,但我会考虑它,如果这是唯一的方法.我唯一的混淆是在上面的例子中“EE13EA1E-D5F4-4E38-986D-3F4B0B03AEE4”部分来自哪里.为了重建一个有效的URI,我该如何访问该值?

  3. 防止序列为空

    作者:EricaSadun,原文链接,原文日期:2016-05-11译者:pucca;校对:wiilen;定稿:CMB昨天在Swift-Users有人提问如何防止序列为空。这个问题来源于如何在断言中测试一个序列,由此引发的问题是如果序列为空,会返回true来满足断言。我们先不考虑这种处理是否有问题,JeremyPereira提出了一个相当巧妙的解决方案:但从此讨论中延伸出的另一个大问题是“如何优雅地判断一个序列是否为空?”。本文由SwiftGG翻译组翻译,已经获得作者翻译授权,最新文章请访问http://

  4. 为什么在Swift中甚至需要便利关键字?

    由于Swift支持方法和初始化程序重载,因此可以将多个init放在一起,并使用任何您认为方便的:那么为什么便利关键字也存在呢?`我在thisanswer年轻微触及它,其中我详细地覆盖了Swift的初始化规则,但主要关注的是所需的词。因为Swift不允许未初始化的变量,所以不能保证从你继承的类继承所有(或任何)初始化器。所以方便关键字做的是告诉我们哪些初始化器可以被添加没有默认值的实例变量的子类继承。它只意味着Inheritor只有一个指定的初始化器。

  5. swift – 检查Hashable一致性

    我有一个基本协议(模型),一些结构符合.它们也符合Hashable我有一个函数,它接受一个符合Model的对象数组.如何将[Model]传递给需要Hashables而不制作ModelHashable的函数?我试图避免这种情况因为当我这样做时,我得到“模型不能用作通用约束……”

  6. swift – 如何URI编码图像?

    究竟是什么意思?这是否意味着将图像转换为base64字符串,然后将其传递给请求?

  7. swift – Sprite-kit:在圆形路径中移动元素

    我正试图让元素在cercle的边缘移动.>我已经在屏幕中间创建并定位了一个cercle:>然后我创建了另一个精灵并在其上添加了一个动作:宇宙飞船的第一次旋转(见下图)完全遵循圆的边缘,但第二次迭代改变了宇宙飞船的位置并将其移出屏幕边界.这是正常的还是我做错了什么?

  8. 如何在Swift中用它创建类和实例对象的数组?

    能够实例化适当的对象?您只需要一个必需的init,一些方法来创建一个实例,该实例将在您希望其工作的所有类型之间共享.保存init方法的协议可以正常工作.显然,当init不需要参数时,这种方法最简单.缺点是您需要向下转换结果实例.

  9. 如何在Android 4.0中的HTML5VFullScreen $SurfaceVideoView中获取HTML5视频URI?

    我想在用户点击视频控制栏中的全屏按钮时获取HTML5视频URI.根据this,Android4.0中的HTML5视频视图是SurfaceView,而不是VideoView.有人能告诉我如何在SurfaceVideoView中获取URI吗?这是我的代码.非常感谢.解决方法容易,使用反射.把它放在onShowCustomView()方法中:

  10. uri – 将android手机号码标签ID翻译成字符串

    嗨,我正在写一个小型的Android应用程序,密切工作的白色手机标签,但我不明白我是如何调整翻译Documentation中描述的uri值.我想要做的是将TYPE_HOME转换为Home等等.我目前的解决方案是列出所有已翻译的字符串,但它已经提出了很多问题.但我希望能够像地址簿和其他应用程序一样使用它.解决方法Android有一个内置的方法来做到这一点……

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部