2012-02-23 12 views
6

Utilizando Java, quiero quitar el identificador de fragmento y realizar una normalización simple (por ejemplo, esquemas en minúscula, hosts) de un conjunto diverso de URI. Los URI de entrada y salida deben ser equivalentes en un sentido HTTP general.Normalización de cadenas de URI posiblemente codificadas en Java

Normalmente, esto debería ser sencillo. Sin embargo, para URI como http://blah.org/A_%28Secret%29.xml#blah, cuyo porcentaje codifica (Secret), el comportamiento de java.util.URI hace la vida más difícil.

El método de normalización debe devolver http://blah.org/A_%28Secret%29.xml ya que los URI http://blah.org/A_%28Secret%29.xml y http://blah.org/A_(Secret).xml no son equivalentes en interpretación [§2.2; RFC3968]

Así que tienen los dos siguientes métodos de normalización:

URI u = new URI("http://blah.org/A_%28Secret%29.xml#blah"); 
System.out.println(u); 
     // prints "http://blah.org/A_%28Secret%29.xml#blah" 

String path1 = u.getPath();  //gives "A_(Secret).xml" 
String path2 = u.getRawPath(); //gives "A_%28Secret%29.xml" 


//NORMALISE METHOD 1 
URI norm1 = new URI(u.getScheme().toLowerCase(), u.getUserInfo(), 
         u.getHost().toLowerCase(), u.getPort(), path1, 
         u.getQuery(), null); 
System.out.println(norm1); 
// prints "http://blah.org/A_(Secret).xml" 

//NORMALISE METHOD 2 
URI norm2 = new URI(u.getScheme().toLowerCase(), u.getUserInfo(), 
         u.getHost().toLowerCase(), u.getPort(), path2, 
         u.getQuery(), null); 
System.out.println(norm2); 
// prints "http://blah.org/A_%2528Secret%2529.xml" 

Como vemos, el URI se analiza y se reconstruyeron sin el identificador de fragmento.

Sin embargo, para el método 1, u.getPath() devuelve un URI no codificado, que cambia el URI final.

Para el método 2, u.getRawPath() devuelve la ruta de acceso original, pero cuando se pasa al constructor URI, Java decide agregar una codificación doble.

Esto se siente como una trampa para los dedos en chino.

preguntas por lo que dos principales:

  • java.util.URI ¿Por qué sienten la necesidad de jugar con la codificación?
  • ¿Cómo se puede implementar este método de normalización sin alterar el porcentaje de codificación original?

(preferiría no tener que implementar los métodos de análisis/concatenación de java.util.URI, que son no trivial.)


EDIT: Aquí hay alguna información más lejos de URI javadoc.

  • El single-argumento del constructor requiere los caracteres no válidos en su argumento cotizado y conserva ninguna octetos y otros personajes que están presentes escaparon.

  • Los constructores de argumento múltiple citan caracteres ilegales según lo requieren los componentes en los que aparecen. El carácter de porcentaje ('%') siempre es citado por estos constructores. Cualquier otro carácter se conserva.

  • El getRawUserInfo, getRawPath, getRawQuery, getRawFragment, getRawAuthority, y getRawSchemeSpecificPart métodos devuelven los valores de sus componentes correspondientes en forma cruda, sin interpretar cualquier octetos escaparon. Las cadenas devueltas por estos métodos pueden contener tanto octetos escapados como otros caracteres, y no contendrán ningún carácter ilegal.

  • El GetUserInfo, getPath, getQuery, getFragment, getAuthority, y getSchemeSpecificPart métodos decodificar cualquier octetos escaparon en sus componentes correspondientes. Las cadenas devueltas por estos métodos pueden contener tanto otros caracteres como caracteres ilegales, y no contendrán ningún octeto escapado.

  • El método toString devuelve una cadena de URI con todas las comillas necesarias pero que pueden contener otros caracteres.

  • El método toASCIIString devuelve una cadena de URI completamente citada y codificada que no contiene ningún otro carácter.

Así que no puedo utilizar el constructor de múltiples argumento sin tener la codificación URL metido con internamente por la clase URI. Pah!

+0

El caso de uso es un rastreador. Nos gustaría tomar un conjunto de URI extraídos y "normalizarlos" en un conjunto lo más pequeño posible, sin dejar de garantizar que el contenido recuperado sea el mismo.(La pregunta http://stackoverflow.com/questions/2993649/how-to-normalize-a-url-in-java está relacionada, pero no aborda el problema de eliminar identificadores de fragmentos, con la codificación de URL cambiando). – badroit

+1

Yo soy lejos de las cosas de URI, y no estoy seguro si lo necesita de la manera estándar con la API de URI, pero si solo quisiera implementar esta funcionalidad de alguna manera, yo podría 1) Obtener la subcadena de la url original hasta el primera aparición de '#' o '?' o '&' ya que eso es lo que realmente separa la URL de la información adicional o 2) Permita que el URI cree la uri normal (norm2 en el ejemplo) y luego reemplace todo el% con la original uno en la secuencia posicional (1º de norm2 con 1º de original, etc.). Por supuesto, esto es solo si la forma estándar no es utilizable. –

Respuesta

9

Debido java.net.URI se introduce en Java 1.4 (que sale en 2002) y se basa en RFC2396 que trata '(' y ')' como caracteres que no necesitan de escape y la semántica no cambia incluso si se escapó, además incluso dice que no se debe escapar a menos que sea necesario (§2.3, RFC2396).

Pero RFC3986 (que sale en 2005) cambió esto, y supongo que los desarrolladores de JDK deciden no cambiar el comportamiento de java.net.URI por la compatibilidad del código existente.

Al azar en Google, encontré Jena IRI se ve bien.

public class IRITest { 
public static void main(String[] args) { 
    IRIFactory factory = IRIFactory.uriImplementation(); 
    IRI iri = factory.construct("http://blah.org/A_%28Secret%29.xml#blah"); 
    ArrayList<String> a = new ArrayList<String>(); 
    a.add(iri.getScheme()); 
    a.add(iri.getRawUserinfo()); 
    a.add(iri.getRawHost()); 
    a.add(iri.getRawPath()); 
    a.add(iri.getRawQuery()); 
    a.add(iri.getRawFragment()); 
    IRI iri2 = factory.construct("http://blah.org/A_(Secret).xml#blah"); 
    ArrayList<String> b = new ArrayList<String>(); 
    b.add(iri2.getScheme()); 
    b.add(iri2.getRawUserinfo()); 
    b.add(iri2.getRawHost()); 
    b.add(iri2.getRawPath()); 
    b.add(iri2.getRawQuery()); 
    b.add(iri2.getRawFragment()); 

    System.out.println(a); 
    //[http, null, blah.org, /A_%28Secret%29.xml, null, blah] 
    System.out.println(b); 
    //[http, null, blah.org, /A_(Secret).xml, null, blah] 
} 
} 
4

Tenga en cuenta este pasaje al final de [§2.2; RFC3968]

URI aplicaciones que producen debe octetos de datos por ciento a codificar que corresponden a caracteres en el conjunto reservado a menos que estos caracteres se permite específicamente por el esquema de URI para representar los datos en ese componente . Si se encuentra un carácter reservado en un componente URI y no se conoce ningún rol delimitador para ese carácter, entonces debe ser interpretado como representando el octeto de datos correspondiente a la codificación del carácter en US-ASCII.

Por lo tanto, siempre que el esquema sea http o https, la codificación es el comportamiento correcto.

Pruebe utilizar el método toASCIIString en lugar de toString para imprimir el URI. Por ejemplo:

System.put.println(norm1.toASCIIString()); 
+0

¡Gracias por la información! No estoy seguro de estar de acuerdo con tu interpretación del pasaje. Esta parte: "* a menos que estos caracteres estén específicamente permitidos por el esquema URI para representar datos en ese componente *" sugiere que no es necesario para HTTP/HTTPS que permiten, por ejemplo, '"() "' caracteres. En cualquier caso, la pregunta es irrelevante para un rastreador si considera el pasaje "* El porcentaje de codificación de un carácter reservado, o la decodificación de un octeto porcentual codificado que corresponde a un carácter reservado, cambiará la manera en que la mayoría de las aplicaciones interpretan el URI. * ". (El método 'toASCIIString' no tiene ningún efecto aquí.) – badroit