我相信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”中,该方法只适用于完整路径。但是我需要相对路径,因为文件夹位置会有所不同。 目标文件位于项目文件夹中。是否有其他方法可以用来读取文件内容,而不需要绝对路径? 提前多谢。下面是代码