是的,我同意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.18public 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=aaaaUriBuilder.replaceQuery(String) MATCHES: http://example.com/something/more/long?query=http://local:282/rand&action=aaaaUriBuilder.uri(URI) MATCHES: http://example.com/something/more/long?query=http://local:282/rand&action=aaaa



