Agregar URL relativa a java.net.URL

7 minutos de lectura

avatar de usuario de fulmicoton
fulmicotón

Siempre que tenga un objeto java.net.URL, apuntando a digamos

http://example.com/myItems o http://example.com/myItems/

¿Hay algún ayudante en algún lugar para agregar alguna URL relativa a esto? Por ejemplo agregar ./myItemId o myItemId Llegar :
http://example.com/myItems/myItemId

  • posible duplicado de Creación de una URL absoluta a partir de una URL relativa en el servlet de Java

    – Don Roby

    21 de septiembre de 2011 a las 10:15

  • Parece que no hay una solución sencilla. La clase URL o URI no admite esto. Apache HttpClient tampoco tiene soporte para esto.

    – Sri

    29 de julio de 2017 a las 2:43

Avatar de usuario de Andrew Duffy
andres duffy

URL tiene un constructor que toma una base URL y un String Especificaciones.

Alternativamente, java.net.URI se adhiere más estrechamente a los estándares, y tiene un resolve método para hacer lo mismo. Crear un URI de tu URL usando URL.toURI.

  • “URL tiene un constructor que toma una URL base y una especificación de cadena”. – No es útil. URL url1 = nueva URL (“petstore.swagger.wordnik.com/api/api-docs”); URL url2 = nueva URL (url1, “/mascota”); System.out.println(url2.toString()) te da: petstore.swagger.wordnik.com/petno petstore.swagger.wordnik.com/api/api-docs/pet

    – usuario2585038

    14/10/2013 a las 17:25

  • En beneficio de los demás: el primer método constructor de URL no funcionó de la manera que esperaba (rtfm, ¿eh?). Solo se utiliza la parte que no es de ruta de la baseURL.

    – Blues de Jasper

    7 de enero de 2016 a las 5:31

  • @user2585038: El segundo parámetro no debe comenzar con una barra inclinada. La barra inclinada indica un enlace “absoluto”, por lo que se descarta la ruta de url1.

    – arsel

    21 de junio de 2016 a las 8:40

  • La resolución de URI en realidad solo funciona por error aquí. vea la respuesta de @Christoph Henkelmann desde abajo

    – Ittai

    12/10/2016 a las 20:17

  • Como mencionó @user2585038, esta respuesta no tiene en cuenta la sección 5.2 (6a) de RFC2396 que dice: “Todo menos el último segmento del componente de ruta del URI base se copia en el búfer. En otras palabras, cualquier carácter después del último carácter de barra (más a la derecha), si lo hay, se excluye”. Este IMO hace que la resolución de URI sea inutilizable en la mayoría de los casos, incluido el del OP.

    – steinybot

    23 de agosto de 2017 a las 22:48

Avatar de usuario de Christoph Henkelmann
Christoph Henkelmann

Este no necesita bibliotecas ni código adicionales y da el resultado deseado:

//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);

Esto imprime:

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

La respuesta aceptada solo funciona si no hay una ruta después del host (en mi humilde opinión, la respuesta aceptada es incorrecta)

  • MalformedUrlException ¿Por qué no lanzan una excepción sin marcar? :'(

    – gkiko

    9 de agosto de 2016 a las 9:03

  • Sí, eso apesta… y es inconsistente: el análisis de números arroja excepciones no verificadas ^^

    –Christoph Henkelmann

    11 de agosto de 2016 a las 14:49

  • Esta solución no maneja barras inclinadas finales. La pregunta proporciona dos ejemplos.

    – rrhrg

    2 de agosto de 2018 a las 17:08

  • url1.getFile().endsWith("/") ? url1.getFile() + "pet" : url1.getFile() + "/pet"?

    –Christoph Henkelmann

    7 de noviembre de 2018 a las 15:01

  • @MartinSenne Bien visto, ajusté el ejemplo.

    –Christoph Henkelmann

    27 de mayo de 2020 a las 14:57

Solo puedes usar el URI clase para esto:

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

Tenga en cuenta la barra inclinada final en la ruta base y el formato relativo a la base del segmento que se está agregando. También puede utilizar el URIBuilder clase del cliente Apache HTTP:

<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() + "https://stackoverflow.com/").resolve("./" + path).getPath());
    return builder.build();
}

  • El resultado del primer bit de código es no http://example.com/basepath/relativepero http://example.com/relativedesafortunadamente haciendo que la primera parte de esta respuesta sea incorrecta.

    – Luciano

    14 oct 2019 a las 8:14

  • En realidad, la primera parte de la respuesta es correcta; funciona si no se proporciona el ./ y si hay más de un / en el primer URI. P.EJ x.com:300/z/y/f.txt/////// ~~~ x.com:300/z/y/f.txt/zzccvr.tyt

    – Ironluca

    5 de marzo de 2020 a las 10:36

avatar de usuario de iain
iain

No puedo creer lo desagradable URI.resolve() realmente está lleno de casos extremos desagradables.

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"

La solución más ordenada que he visto que maneja estos casos extremos de manera predecible es:

URI addPath(URI uri, String path) {
    String newPath;
    if (path.startsWith("/")) newPath = path.replaceAll("//+", "/");
    else if (uri.getPath().endsWith("/")) newPath = uri.getPath() + path.replaceAll("//+", "/");
    else newPath = uri.getPath() + "/" + path.replaceAll("//+", "/");

    return uri.resolve(newPath).normalize();
}

Resultados:

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

jshell> addPath(new URI("http://localhost/"), "//sub/path")
$10 ==> http://localhost/sub/path

jshell> addPath(new URI("http://localhost/"), "//sub/./path")
$11 ==> http://localhost/sub/path

Aquí hay una función de ayuda que he escrito para agregar a la ruta de la URL:

public static URL concatenate(URL baseUrl, String extraPath) throws URISyntaxException, 
                                                                    MalformedURLException {
    URI uri = baseUrl.toURI();
    String newPath = uri.getPath() + "https://stackoverflow.com/" + extraPath;
    URI newUri = uri.resolve(newPath);
    return newUri.toURL();
}

  • Si baseUrl termina con un / o extraPath comienza con un / habrás repetido / en la URL final. Deberías agregar un .normalize() antes del ultimo toURL().

    – Simão Martins

    19 mayo 2017 a las 14:42


  • Esto no funciona para una URL de archivo local que debería comenzar con file://

    -Sundar Rajan

    2 de julio de 2018 a las 6:38

  • si baseUrl.getPath() es "/" ¡¡¡esto modificará el anfitrión!!! URI("http://localhost:80").resolve("//foo") => "http://foo" Muestro cómo abordarlos a continuación.

    – iain

    2 de diciembre de 2021 a las 11:56


Puedes usar URIBuilder y el metodo URI#normalize para evitar la duplicación / en la 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

  • Si baseUrl termina con un / o extraPath comienza con un / habrás repetido / en la URL final. Deberías agregar un .normalize() antes del ultimo toURL().

    – Simão Martins

    19 mayo 2017 a las 14:42


  • Esto no funciona para una URL de archivo local que debería comenzar con file://

    -Sundar Rajan

    2 de julio de 2018 a las 6:38

  • si baseUrl.getPath() es "/" ¡¡¡esto modificará el host!!! URI("http://localhost:80").resolve("//foo") => "http://foo" Muestro cómo abordarlos a continuación.

    – iain

    2 de diciembre de 2021 a las 11:56


avatar de usuario de twhitbeck
twitbeck

He buscado por todas partes una respuesta a esta pregunta. La única implementación que puedo encontrar está en el SDK de Android: Uri.Constructor. Lo he extraído para mis propios fines.

private String appendSegmentToPath(String path, String segment) {
  if (path == null || path.isEmpty()) {
    return "https://stackoverflow.com/" + segment;
  }

  if (path.charAt(path.length() - 1) == "https://stackoverflow.com/") {
    return path + segment;
  }

  return path + "https://stackoverflow.com/" + segment;
}

Este es donde encontré la fuente.

En conjunto con Apache URIBuilderasí es como lo estoy usando: builder.setPath(appendSegmentToPath(builder.getPath(), segment));

¿Ha sido útil esta solución?