当前位置: 首页 > 面试题库 >

当相对URI包含空路径时,Java的URI.resolve是否与RFC 3986不兼容?

符献
2023-03-14
问题内容

我相信Java的URI.resolve方法的定义和实现与RFC
3986第5.2.2节
不兼容。我知道Java
API定义了该方法的工作方式,如果现在进行更改,它将破坏现有的应用程序,但是我的问题是: 谁能证实我的理解,该方法与RFC 3986不兼容?

我使用的是来自以下问题的示例:java.net.URI仅针对查询字符串解析,我将在此处复制:

我正在尝试使用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, 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第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
” 页面上指向“?query”的链接会转到“ 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

虽然正确,但这不是一个完整的答案,因为原始问题 不是在询问
路径
(即上面的“嘶嘶声”)。相反,问题与相对URI引用的单独查询组件有关。示例代码中使用的URI类构造函数接受五个不同的String参数,并且除queryString参数之外的所有参数都以形式传递null。(请注意,Java接受null字符串作为path参数,这在逻辑上将导致“空”路径组件,因为“尽管路径组件可能为空(长度为零),但路径从未定义
” )。这在以后很重要。

Sajan Chandran 在先前的评论中指出,java.net.URI该类是为实现RFC
2396而编写的,而 不是 问题RFC
3986的主题。前者在2005年被后者 淘汰
了。URI类Javadoc没有提到较新的RFC可以解释为它不兼容的更多证据。让我们继续讨论:

  • JDK-6791060是一个未解决的问题,建议该类“应该为RFC 3986更新”。此处有一条警告警告说:“ RFC3986不能完全向后兼容2396”。

  • 先前曾尝试将URI类的某些部分更新为与RFC 3986兼容,例如JDK-6348622,但随后进行了回滚以打破向后兼容性。(另请参见JDK邮件列表上的讨论。)

  • 正如SubOptimal指出的那样,尽管路径“合并”逻辑听起来很相似,但较新的RFC中指定的伪代码与实际实现不匹配。在伪代码中,当相对URI的路径为 空时将从原始URI 原样 复制生成的 目标路径 。在那些条件下不执行“合并”逻辑。与该规范相反,Java的URI实现在问题的最后一个字符后 修剪了基本路径/

如果要RFC 3986行为,则可以使用URI类的替代方法。Java EE
6实现提供了javax.ws.rs.core.UriBuilder(在Jersey
1.18中)似乎表现出预期的效果(请参见下文)。就编码不同的URI组件而言,它至少要求了解RFC。

在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, queryString, 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


 类似资料:
  • 问题内容: 我有/root/update/test.php文件。还有一个文件/root/connect.php; 该文件有一行 在/root/update/test.php中。有代码 当我运行/root/update/test.php时,它找到connect.php,但是找不到config.php,这给了我 这让我感到困惑,因为这些警告使我似乎似乎在正确地进行所有操作-包含路径为/ root,并且

  • 我目前正在制作一个与phpBB论坛融合的网站。根据phpBB3会话集成的官方解决方案,我使用了以下代码来包含phpBB安装中的相关文件: 我的论坛文件夹是(根路径)/forums/但是,如果我尝试包含另一个文件夹中的代码,它会抛出以下错误: [phpBB Debug] PHP警告:在文件/home/unrealsp/public _ html/includes/phpBB . PHP第5行:inc

  • 问题内容: 我在理解有关PHP相对包含路径的规则集时遇到了麻烦。如果我运行文件A.PHP-,文件A.PHP包含文件B.PHP,其中文件C.PHP,则C.PHP的相对路径应该与B.PHP的位置有关,还是与A的位置有关.PHP?也就是说,从哪个 文件 调用include或仅从当前工作目录是什么以及确定当前工作目录的文件有关系吗? 问题答案: 它相对于主脚本,在本例中是A.php。请记住,只是将代码插入

  • 问题内容: 如果使用绝对路径,则无法将整个目录移动到新位置。如果使用相对路径,则无法将单个文件移动到新位置。 这里有什么解决方案?您是否设置了一个包含根路径并从那里开始的配置文件?还是您有类似的规则:永不移动文件? 我在某些项目中看到人们使用dirname( FILE )。我的意思是,为什么不简单地将其删除,因为目录名还是相对的(取决于文件所在的位置)? 问题答案: 您应该使用一个配置文件,该配置

  • 问题内容: 这不起作用-vm%JAVA_HOME%/ bin / javaw.exe 当路径包含空格(“程序文件”目录)时,如何在Windows 8上用完整路径替换%JAVA_HOME% 问题答案: 你有试过吗 不要将所有内容都放在一行中。 需要放置包含javaw或java可执行文件的文件夹。在带有eclipse 4.7.1的Ubuntu 18下,我能够使其运行: 如果不起作用,请确认您已在中添加

  • 我是Java新手,正在尝试构建一个FX应用程序。我的一个函数旨在用其他字符串替换某些字符串。只要我定义了目标文件的绝对路径,脚本就可以正常工作,但当我使用相对路径时,脚本就会中断。 问题出在方法“ReadAllBytes”中,该方法只适用于完整路径。但是我需要相对路径,因为文件夹位置会有所不同。 目标文件位于项目文件夹中。是否有其他方法可以用来读取文件内容,而不需要绝对路径? 提前多谢。下面是代码