[英]Append relative URL to java.net.URL
假設我有一個 java.net.URL object,指向讓我們說
http://example.com/myItems
或http://example.com/myItems/
append 的某個地方是否有一些幫手,一些親戚 URL 到這個? 例如 append ./myItemId
或myItemId
得到: http://example.com/myItems/myItemId
這個不需要任何額外的庫或代碼並提供所需的結果:
//import java.net.URL;
URL url1 = new URL("http://petstore.swagger.wordnik.com/api/api-docs?foo=1&bar=baz");
URL url2 = new URL(url1.getProtocol(), url1.getHost(), url1.getPort(), url1.getPath() + "/pet" + "?" + url1.getQuery(), null);
System.out.println(url1);
System.out.println(url2);
這打印:
http://petstore.swagger.wordnik.com/api/api-docs?foo=1&bar=baz
http://petstore.swagger.wordnik.com/api/api-docs/pet?foo=1&bar=baz
接受的答案僅在主機之后沒有路徑時才有效(恕我直言,接受的答案是錯誤的)
這是我編寫的要添加到 url 路徑的輔助函數:
public static URL concatenate(URL baseUrl, String extraPath) throws URISyntaxException,
MalformedURLException {
URI uri = baseUrl.toURI();
String newPath = uri.getPath() + '/' + extraPath;
URI newUri = uri.resolve(newPath);
return newUri.toURL();
}
我已經廣泛搜索了這個問題的答案。 我能找到的唯一實現是在 Android SDK 中: Uri.Builder 。 我出於自己的目的提取了它。
private String appendSegmentToPath(String path, String segment) {
if (path == null || path.isEmpty()) {
return "/" + segment;
}
if (path.charAt(path.length() - 1) == '/') {
return path + segment;
}
return path + "/" + segment;
}
這是我找到來源的地方。
結合Apache URIBuilder ,這就是我使用它的方式: builder.setPath(appendSegmentToPath(builder.getPath(), segment));
更新
我相信這是最短的解決方案:
URL url1 = new URL("http://domain.com/contextpath");
String relativePath = "/additional/relative/path";
URL concatenatedUrl = new URL(url1.toExternalForm() + relativePath);
您可以使用URIBuilder和方法URI#normalize
來避免在 URI 中重復/
:
URIBuilder uriBuilder = new URIBuilder("http://example.com/test");
URI uri = uriBuilder.setPath(uriBuilder.getPath() + "/path/to/add")
.build()
.normalize();
// expected : http://example.com/test/path/to/add
您可以為此使用URI
類:
import java.net.URI;
import org.apache.http.client.utils.URIBuilder;
URI uri = URI.create("http://example.com/basepath/");
URI uri2 = uri.resolve("./relative");
// => http://example.com/basepath/relative
請注意基本路徑上的尾部斜杠和所附加段的基本相對格式。 您還可以使用來自 Apache HTTP 客戶端的URIBuilder
類:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
...
import java.net.URI;
import org.apache.http.client.utils.URIBuilder;
URI uri = URI.create("http://example.com/basepath");
URI uri2 = appendPath(uri, "relative");
// => http://example.com/basepath/relative
public URI appendPath(URI uri, String path) {
URIBuilder builder = new URIBuilder(uri);
builder.setPath(URI.create(builder.getPath() + "/").resolve("./" + path).getPath());
return builder.build();
}
將相對路徑連接到 URI:
java.net.URI uri = URI.create("https://stackoverflow.com/questions")
java.net.URI res = uri.resolve(uri.getPath + "/some/path")
res
將包含https://stackoverflow.com/questions/some/path
在 Android 上,您可以使用android.net.Uri
。 下面允許從現有的 URL 創建一個Uri.Builder
作為String
,然后附加:
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”是否以 1 結尾,否則在路徑之前添加一個。
根據文檔,這支持
遵循模式的絕對分層 URI 引用
<scheme>://<authority><absolute path>?<query>#<fragment>
帶模式的相對 URI
<relative or absolute path>?<query>#<fragment>
//<authority><absolute path>?<query>#<fragment>
帶有模式的不透明 URI
<scheme>:<opaque part>#<fragment>
使用 Apache URIBuilder http://hc.apache.org/httpcomponents-client-4.3.x/httpclient/apidocs/org/apache/http/client/utils/URIBuilder.html 的一些示例:
例1:
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
我基於 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());
}
}
我在 URI 的編碼方面遇到了一些困難。 追加對我不起作用,因為它是 content:// 類型並且它不喜歡“/”。 這個解決方案假設沒有查詢,也沒有片段(畢竟我們正在使用路徑):
科特林代碼:
val newUri = Uri.parse(myUri.toString() + Uri.encode("/$relPath"))
下面給出了一個沒有任何外部庫的實用解決方案。
(評論:在閱讀了迄今為止給出的所有答案后,我對所提供的解決方案真的不滿意 - 特別是因為這個問題已經有八年歷史了。沒有解決方案可以正確處理查詢、片段等。)
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
class URLHelper {
public static URL appendRelativePathToURL(URL base, String relPath) {
/*
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
| _____________________|__
/ \ / \
urn:example:animal:ferret:nose
see https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
*/
try {
URI baseUri = base.toURI();
// cut initial slash of relative path
String relPathToAdd = relPath.startsWith("/") ? relPath.substring(1) : relPath;
// cut trailing slash of present path
String path = baseUri.getPath();
String pathWithoutTrailingSlash = path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
return new URI(baseUri.getScheme(),
baseUri.getAuthority(),
pathWithoutTrailingSlash + "/" + relPathToAdd,
baseUri.getQuery(),
baseUri.getFragment()).toURL();
} catch (URISyntaxException e) {
throw new MalformedURLRuntimeException("Error parsing URI.", e);
} catch (MalformedURLException e) {
throw new MalformedURLRuntimeException("Malformed URL.", e);
}
}
public static class MalformedURLRuntimeException extends RuntimeException {
public MalformedURLRuntimeException(String msg, Throwable cause) {
super("Malformed URL: " + msg, cause);
}
}
}
private void demo() {
try {
URL coolURL = new URL("http://fun.de/path/a/b/c?query&another=3#asdf");
URL notSoCoolURL = new URL("http://fun.de/path/a/b/c/?query&another=3#asdf");
System.out.println(URLHelper.appendRelativePathToURL(coolURL, "d"));
System.out.println(URLHelper.appendRelativePathToURL(coolURL, "/d"));
System.out.println(URLHelper.appendRelativePathToURL(notSoCoolURL, "d"));
System.out.println(URLHelper.appendRelativePathToURL(notSoCoolURL, "/d"));
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
對於 android,請確保您使用android.net.Uri
.appendPath()
public String joinUrls(String baseUrl, String extraPath) {
try {
URI uri = URI.create(baseUrl+"/");//added additional slash in case there is no slash at either sides
URI newUri = uri.resolve(extraPath);
return newUri.toURL().toString();
} catch (IllegalArgumentException | MalformedURLException e) {
//exception
}
}
一個手工制作的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 null;
} else if (segmentArray.length == 1) {
return segmentArray[0];
} else {
boolean previousEnds;
boolean currentStarts;
StringBuilder sb = new StringBuilder();
sb.append(segmentArray[0]);
for (int i = 1; i < segmentArray.length; i++) {
previousEnds = segmentArray[i - 1].endsWith("/");
currentStarts = segmentArray[i].startsWith("/");
if (!previousEnds && !currentStarts) {
sb.append("/").append(segmentArray[i]);
} else if (previousEnds && currentStarts) {
sb.append(segmentArray[i].substring(1));
} else {
sb.append(segmentArray[i]);
}
}
return sb.toString();
}
}
在 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://stackoverflow.com/questions/7498030/append-relative-url
Maven 依賴:
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.1</version>
</dependency>
我無法相信URI.resolve()
真的充滿了令人討厭的邊緣情況。
new URI("http://localhost:80").resolve("foo") => "http://localhost:80foo"
new URI("http://localhost:80").resolve("//foo") => "http://foo"
new URI("http://localhost:80").resolve(".//foo") => "http://foo"
我見過的以可預測的方式處理這些邊緣情況的最整潔的解決方案是:
URI addPath(URI uri, String path) {
String newPath;
if (path.startsWith("/")) newPath = path;
else if (uri.getPath().endsWith("/")) newPath = uri.getPath() + path;
else newPath = uri.getPath() + "/" + path;
return uri.resolve(newPath).normalize();
}
結果:
jshell> addPath(new URI("http://localhost"), "sub/path")
$3 ==> http://localhost/sub/path
jshell> addPath(new URI("http://localhost/"), "sub/path")
$4 ==> http://localhost/sub/path
jshell> addPath(new URI("http://localhost/"), "/sub/path")
$5 ==> http://localhost/sub/path
jshell> addPath(new URI("http://localhost/random-path"), "/sub/path")
$6 ==> http://localhost/sub/path
jshell> addPath(new URI("http://localhost/random-path"), "./sub/path")
$7 ==> http://localhost/random-path/sub/path
jshell> addPath(new URI("http://localhost/random-path"), "../sub/path")
$8 ==> http://localhost/sub/path
jshell> addPath(new URI("http://localhost"), "../sub/path")
$9 ==> http://localhost/../sub/path
這只需要一行, 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
要繞過所有邊緣情況,最好的辦法是組合兩個標准類 - 來自UriBuilder
和apache.httpclient
的java.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
這適用於任意數量的前導和尾隨/
以及不存在/
的情況。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.