将相对URL添加到java.net.URL

61

假设我有一个指向 http://example.com/myItems 或者 http://example.com/myItems/的java.net.URL对象,是否有某种助手方法可以将一些相对URL添加到它的末尾?例如添加 ./myItemId myItemId 以获取: http://example.com/myItems/myItemId


可能是在Java servlet中从相对URL构建绝对URL的重复问题。 - Don Roby
似乎没有简单的解决方案。无论是URL还是URI类都不支持此功能。Apache HttpClient也不支持此功能。 - Sri
21个回答

2
在Android上可以使用android.net.Uri。以下代码允许从现有的URL字符串创建Uri.Builder,然后进行追加操作:
Uri.parse(baseUrl) // Create Uri from String
    .buildUpon()   // Creates a "Builder"
    .appendEncodedPath("path/to/add")
    .appendQueryParameter("at_ref", "123") // To add ?at_ref=123
    .fragment("anker") // To add #anker
    .build()

请注意,appendEncodedPath 不需要前导的 /,并且仅在 "baseUrl" 结尾没有斜杠时才包含一个检查,否则会在路径之前添加一个斜杠。
根据文档,此方法支持以下内容:
  • 遵循以下模式的绝对分层 URI 引用

    • <scheme>://<authority><absolute path>?<query>#<fragment>
  • 具有以下模式的相对 URI

    • <relative or absolute path>?<query>#<fragment>
    • //<authority><absolute path>?<query>#<fragment>
  • 具有以下模式的不透明 URI

    • <scheme>:<opaque part>#<fragment>
"Original Answer" 翻译成 "最初的回答"

1
我的解决方案基于twhitbeck的答案:

import java.net.URI;
import java.net.URISyntaxException;

public class URIBuilder extends org.apache.http.client.utils.URIBuilder {
    public URIBuilder() {
    }

    public URIBuilder(String string) throws URISyntaxException {
        super(string);
    }

    public URIBuilder(URI uri) {
        super(uri);
    }

    public org.apache.http.client.utils.URIBuilder addPath(String subPath) {
        if (subPath == null || subPath.isEmpty() || "/".equals(subPath)) {
            return this;
        }
        return setPath(appendSegmentToPath(getPath(), subPath));
    }

    private String appendSegmentToPath(String path, String segment) {
        if (path == null || path.isEmpty()) {
            path = "/";
        }

        if (path.charAt(path.length() - 1) == '/' || segment.startsWith("/")) {
            return path + segment;
        }

        return path + "/" + segment;
    }
}

测试:

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class URIBuilderTest {

    @Test
    public void testAddPath() throws Exception {
        String url = "http://example.com/test";
        String expected = "http://example.com/test/example";

        URIBuilder builder = new URIBuilder(url);
        builder.addPath("/example");
        assertEquals(expected, builder.toString());

        builder = new URIBuilder(url);
        builder.addPath("example");
        assertEquals(expected, builder.toString());

        builder.addPath("");
        builder.addPath(null);
        assertEquals(expected, builder.toString());

        url = "http://example.com";
        expected = "http://example.com/example";

        builder = new URIBuilder(url);
        builder.addPath("/");
        assertEquals(url, builder.toString());
        builder.addPath("/example");
        assertEquals(expected, builder.toString());
    }
}

要点:https://gist.github.com/enginer/230e2dc2f1d213a825d5


1

我在处理URI编码时遇到了一些困难。由于它的类型是content://,所以添加操作对我来说并不起作用,而且它不喜欢“/”符号。此解决方案假定没有查询或片段(毕竟我们是在处理路径):

Kotlin代码:

  val newUri = Uri.parse(myUri.toString() + Uri.encode("/$relPath"))

1
一些使用Apache URIBuilder的示例 http://hc.apache.org/httpcomponents-client-4.3.x/httpclient/apidocs/org/apache/http/client/utils/URIBuilder.html
Ex1:
String url = "http://example.com/test";
URIBuilder builder = new URIBuilder(url);
builder.setPath((builder.getPath() + "/example").replaceAll("//+", "/"));
System.out.println("Result 1 -> " + builder.toString());

结果1 -> http://example.com/test/example

例2:

String url = "http://example.com/test";
URIBuilder builder = new URIBuilder(url);
builder.setPath((builder.getPath() + "///example").replaceAll("//+", "/"));
System.out.println("Result 2 -> " + builder.toString());

结果 2 -> http://example.com/test/example


1

这只需要一行代码,normalize() 在这里是你的好朋友,并且在连接之间始终添加一个额外的 /

当 baseUrl 以 / 结尾时,normalize() 将删除多余的。如果它不以 / 结尾,则通过故意添加一个来解决。

String unknownBaseUrl = "https://example.com/apples/";
String result = URI.create(unknownBaseUrl + "/" + "1209").normalize().toString();
System.out.println(result);

output: https://example.com/apples/1209

带有许多额外 / 的示例将根据 RFC 2396 进行规范化,以得到一个合理的路径。

String unknownBaseUrl = "https://example.com/apples/";
String result = URI.create(unknownBaseUrl + "/" + "/1209").normalize().toString();
System.out.println(result);

output: https://example.com/apples/1209


1

Apache HttpClient 5.1新增了appendPath方法,支持在URIBuilder中添加路径:

import org.apache.hc.core5.net.URIBuilder;
..
URI uri = new URIBuilder("https://stackoverflow.com/questions")
  .appendPath("7498030")
  .appendPath("append-relative-url")
  .build();

// https://dev59.com/uGs05IYBdhLWcg3wB9af

Maven 依赖:

<dependency>
  <groupId>org.apache.httpcomponents.client5</groupId>
  <artifactId>httpclient5</artifactId>
  <version>5.1</version>
</dependency>

0

使用Spring框架:

import org.springframework.web.util.DefaultUriBuilderFactory;
...
var url = "http://localhost"
var uriBuilderFactory = new DefaultUriBuilderFactory(url)
var target_uri = uriBuilderFactory.builder().pathSegment("path1").pathSegment("path2").build().toString()
//
// result: http://localhost/path1/path2

0
一个手工制作的 URI 段连接器。
public static void main(String[] args) {
    System.out.println(concatURISegments(
            "http://abc/",
            "/dfg/",
            "/lmn",
            "opq"
    ));
}

public static String concatURISegments(String... segmentArray) {
    if (segmentArray.length == 0) {
        return "";
    } else if (segmentArray.length == 1) {
        return segmentArray[0];
    }

    List<String> segmentList = new ArrayList<>();
    for (String s : segmentArray) {
        if (s != null && s.length() > 0) {
            segmentList.add(s);
        }
    }

    if (segmentList.size() == 0) {
        return "";
    } else if (segmentList.size() == 1) {
        return segmentList.get(0);
    }

    StringBuilder sb = new StringBuilder();
    sb.append(segmentList.get(0));
    String prevS;
    String currS;
    boolean prevB;
    boolean currB;
    for (int i = 1; i < segmentList.size(); i++) {
        prevS = segmentList.get(i - 1);
        currS = segmentList.get(i);
        prevB = prevS.endsWith("/");
        currB = currS.startsWith("/");
        if (!prevB && !currB) {
            sb.append("/").append(currS);
        } else if (prevB && currB) {
            sb.append(currS.substring(1));
        } else {
            sb.append(currS);
        }
    }
    return sb.toString();
}

0

对于 Android,请确保使用 android.net.Uri 中的 .appendPath() 方法。


0
为了解决所有边缘情况,最好的方法是结合两个标准类 - apache.httpclientUriBuilderjava.nio.file.Paths
String rootUrl = "http://host:80/root/url";
String relativePath = "relative/path";

URIBuilder builder = new URIBuilder(rootUrl);
String combinedPath = Paths.get(builder.getPath(), relativePath).toString();
builder.setPath(combinedPath);

URL targetUrl = builder.build().toURL();

结果为:http://host:80/root/url/relative/path

这适用于任意数量的前导和尾随/,并且在缺少/时也可以使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接