我认为 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 对象,一个查询(在字符串中)。例如:
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);
理论(或者我的想法)是 resolve 应该返回:
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”页面上到“?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是:queryString 虽然正确,但它不是一个完整的答案,因为原始问题不是询问 path (即上面的“fizz”)。相反,问题与相对 URI 引用的单独 query component 有关。 URI 类 constructor used in the example code 接受五个不同的字符串参数,并且除了 null 参数之外的所有参数都作为 java.net.URI 传递。 (请注意,Java 接受空字符串作为路径参数,这在逻辑上会导致“空”路径组件,因为“the path component is never undefined ”虽然它是“may be empty (zero length) ”。)这在以后很重要。
在 earlier comment 中,Sajan Chandran 指出 / class 被记录为实现 RFC 2396 而不是问题的主题 RFC 3986 。前者在 2005 年被后者淘汰。URI 类 Javadoc 没有提到较新的 RFC 可以解释为它不兼容的更多证据。让我们再补充一些:
JDK-6791060 建议此类“应该针对 RFC 3986 进行更新”。那里的评论警告说“RFC3986 并不完全落后 与 2396 兼容”。它于 2018 年作为 JDK-8019345 的副本关闭(截至 2022 年 10 月仍然开放且未解决,自 2013 年以来没有显着 Activity )。
之前曾尝试更新 URI 类的部分内容以符合 RFC 3986,例如 JDK-6348622 ,但随后 rolled back 破坏了向后兼容性。 (另请参阅 JDK 邮件列表中的 this discussion。)
虽然路径“合并”逻辑听起来与 noted by SubOptimal 相似,但较新的 RFC 中指定的伪代码与 actual implementation 不匹配。在伪代码中,当相对 URI 的路径为空时,生成的目标路径将按原样从基本 URI 复制。伪代码的“合并”逻辑不会在这些条件下执行。与该规范相反,Java 的 URI 实现在最后一个 javax.ws.rs.core.UriBuilder 字符之后修剪基本路径,如问题中所观察到的。
如果您想要 RFC 3986 行为,可以使用 URI 类的替代方法。 Java EE 6 到 EE 8 实现提供了 jakarta.ws.rs.core.UriBuilder ,它(在 Jersey 1.18 中)的行为似乎符合您的预期(见下文)。就编码不同的 URI 组件而言,它至少声称了解 RFC。随着从 JavaEE 到 JakartaEE 9 的切换(大约 2020 年),此类转移到 ojit_code 。
在 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
关于java - 当相对 URI 包含空路径时,Java 的 URI.resolve 是否与 RFC 3986 不兼容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22203111/
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg