HTTP-URL-Adresscodierung in Java

366

Meine eigenständige Java-Anwendung erhält vom Benutzer eine URL (die auf eine Datei verweist), und ich muss sie treffen und herunterladen. Das Problem ist, dass ich die HTTP-URL-Adresse nicht richtig codieren kann ...

Beispiel:

URL:  http://search.barnesandnoble.com/booksearch/first book.pdf

java.net.URLEncoder.encode(url.toString(), "ISO-8859-1");

gibt mich zurück:

http%3A%2F%2Fsearch.barnesandnoble.com%2Fbooksearch%2Ffirst+book.pdf

Aber was ich will ist

http://search.barnesandnoble.com/booksearch/first%20book.pdf

(Leerzeichen durch% 20 ersetzt)

Ich denke, es URLEncoderist nicht dafür ausgelegt, HTTP-URLs zu codieren ... Das JavaDoc sagt "Utility-Klasse für die HTML-Formularcodierung" ... Gibt es eine andere Möglichkeit, dies zu tun?

suDocker
quelle
Das Verhalten ist völlig korrekt. Mit der URL-Codierung wird etwas in eine Zeichenfolge umgewandelt, die sicher als URL-Parameter übergeben werden kann und überhaupt nicht als URL interpretiert wird. Während Sie möchten, dass nur ein kleiner Teil der URL konvertiert wird.
Stephen Holt

Antworten:

303

Die Klasse java.net.URI kann helfen. in der Dokumentation der URL finden Sie

Beachten Sie, dass die URI-Klasse unter bestimmten Umständen ein Escapezeichen für ihre Komponentenfelder ausführt. Die empfohlene Methode zum Verwalten der Codierung und Decodierung von URLs ist die Verwendung eines URI

Verwenden Sie einen der Konstruktoren mit mehr als einem Argument, z.

URI uri = new URI(
    "http", 
    "search.barnesandnoble.com", 
    "/booksearch/first book.pdf",
    null);
URL url = uri.toURL();
//or String request = uri.toString();

(Der Einzelargument-Konstruktor von URI entgeht NICHT illegalen Zeichen.)


Nur illegale Zeichen werden durch den obigen Code maskiert - es werden KEINE Nicht-ASCII-Zeichen maskiert (siehe Kommentar von fatih).
Die toASCIIStringMethode kann verwendet werden, um einen String nur mit US-ASCII-Zeichen abzurufen:

URI uri = new URI(
    "http", 
    "search.barnesandnoble.com", 
    "/booksearch/é",
    null);
String request = uri.toASCIIString();

http://www.google.com/ig/api?weather=São PauloVerwenden Sie für eine URL mit einer Abfrage wie die 5-Parameter-Version des Konstruktors:

URI uri = new URI(
        "http", 
        "www.google.com", 
        "/ig/api",
        "weather=São Paulo",
        null);
String request = uri.toASCIIString();
user85421
quelle
13
Bitte beachten Sie, dass die hier erwähnte URI-Klasse von "org.apache.commons.httpclient.URI" stammt, nicht von "java.net". Die URI "java.net" akzeptiert keine unzulässigen Zeichen, es sei denn, Sie verwenden Konstruktoren, die URL aus ihren Komponenten
erstellen
7
@Mohamed: Die Klasse, die ich erwähnt und zum Testen verwendet habe, ist java.net.URI : Sie hat perfekt funktioniert (Java 1.6). Ich würde den vollständig qualifizierten Klassennamen erwähnen, wenn es sich nicht um den Standard-Java-Namen handelt und der Link auf die Dokumentation von verweist java.net.URI. Und nach dem Kommentar von Sudhakar löste es das Problem, ohne irgendwelche "Commons-Bibliotheken" einzuschließen!
user85421
1
URI uri = neuer URI ("http", "search.barnesandnoble.com", "/ booksearch / é", null); Ist das Entkommen mit dieser Probe nicht korrekt? Dies sollte mit%
Escape
@fatih - das stimmt, danke! Normalerweise sollte das kein Problem sein, aber es gibt eine einfache Lösung - fast die gleiche wie ich zuvor geschrieben habe. Siehe 2. Bearbeitung.
user85421
@ Carlos Thx für die Bearbeitung. Jetzt entkommt es, aber nicht richtig. Es sollte ein% zum HEX-Wert von char für Pfadparameter hinzugefügt werden, was bedeutet, dass é char
fmucar
91

Bitte beachten Sie, dass die meisten der oben genannten Antworten falsch sind.

Die URLEncoderKlasse ist trotz ihres Namens NICHT das, was hier sein muss. Es ist bedauerlich, dass Sun diese Klasse so nervig benannt hat. URLEncoderist zum Übergeben von Daten als Parameter gedacht, nicht zum Codieren der URL selbst.

Mit anderen Worten, "http://search.barnesandnoble.com/booksearch/first book.pdf"ist die URL. Parameter wären zum Beispiel "http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that". Die Parameter sind das, wofür Sie sie verwenden würden URLEncoder.

Die folgenden zwei Beispiele zeigen die Unterschiede zwischen den beiden.

Das Folgende erzeugt die falschen Parameter gemäß dem HTTP-Standard. Beachten Sie, dass das kaufmännische Und (&) und das Pluszeichen (+) falsch codiert sind.

uri = new URI("http", null, "www.google.com", 80, 
"/help/me/book name+me/", "MY CRZY QUERY! +&+ :)", null);

// URI: http://www.google.com:80/help/me/book%20name+me/?MY%20CRZY%20QUERY!%20+&+%20:)

Im Folgenden werden die richtigen Parameter erstellt, wobei die Abfrage ordnungsgemäß codiert wird. Beachten Sie die Leerzeichen, kaufmännischen Und-Zeichen und Pluszeichen.

uri = new URI("http", null, "www.google.com", 80, "/help/me/book name+me/", URLEncoder.encode("MY CRZY QUERY! +&+ :)", "UTF-8"), null);

// URI: http://www.google.com:80/help/me/book%20name+me/?MY+CRZY+QUERY%2521+%252B%2526%252B+%253A%2529
Matt
quelle
2
Richtig, der URI-Konstruktor codiert den Querystring bereits gemäß der Dokumentation docs.oracle.com/javase/1.4.2/docs/api/java/net/… , java.lang.String, java.lang.String, int , java.lang.String, java.lang.String, java.lang.String)
Madoke
8
@Draemon Die Antwort ist korrekt, verwendet jedoch die Abfragezeichenfolge auf ungewöhnliche Weise. Ein normaleres Beispiel könnte sein query = URLEncoder.encode(key) + "=" + URLEncoder.encode(value). Die Dokumente sagen lediglich, dass "jedes Zeichen, das kein legales URI-Zeichen ist, in Anführungszeichen gesetzt wird".
tc.
1
Ich stimme Matt hier zu. Wenn Sie diese URL in einen Browser eingeben : " google.com/help/me/book name + me /? MY CRZY QUERY! + & + :)", werden die Leerzeichen automatisch codiert, aber das "&" wird als Abfragewert verwendet Trennzeichen und "+" gehen verloren.
Arcot
80

Ich werde hier einen Vorschlag hinzufügen, der sich an Android-Benutzer richtet. Sie können dies tun, ohne externe Bibliotheken abrufen zu müssen. Außerdem sind alle in einigen der obigen Antworten vorgeschlagenen Lösungen zum Suchen / Ersetzen von Zeichen gefährlich und sollten vermieden werden.

Probieren Sie es aus:

String urlStr = "http://abc.dev.domain.com/0007AC/ads/800x480 15sec h.264.mp4";
URL url = new URL(urlStr);
URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
url = uri.toURL();

Sie können sehen, dass in dieser bestimmten URL diese Leerzeichen codiert sein müssen, damit ich sie für eine Anfrage verwenden kann.

Dies nutzt einige Funktionen, die Ihnen in Android-Klassen zur Verfügung stehen. Erstens kann die URL-Klasse eine URL in ihre richtigen Komponenten aufteilen, sodass Sie keine Arbeit zum Suchen / Ersetzen von Zeichenfolgen ausführen müssen. Zweitens nutzt dieser Ansatz die URI-Klassenfunktion, um Komponenten ordnungsgemäß zu maskieren, wenn Sie einen URI über Komponenten anstatt aus einer einzelnen Zeichenfolge erstellen.

Das Schöne an diesem Ansatz ist, dass Sie jede gültige URL-Zeichenfolge verwenden können, ohne dass Sie selbst spezielle Kenntnisse darüber benötigen.

Craig B.
quelle
3
Netter Ansatz, aber ich möchte darauf hinweisen, dass dieser Code eine Doppelcodierung nicht verhindert , z. B. wurde% 20 in% 2520 codiert. Scotts Antwort leidet nicht darunter.
Nattster
2
Es kann nicht damit umgehen #.
Alston
Oder wenn Sie nur Pfadangaben machen möchten: neuer URI (null, null, "/ Pfad mit Leerzeichen", null, null) .toString ()
user1050755
1
@Stallman Wenn Ihr Dateiname # enthält, setzt die URL-Klasse ihn in "ref" (entspricht "fragment" in der URI-Klasse). Sie können feststellen, ob URL.getRef () etwas zurückgibt, das möglicherweise als Teil des Pfads behandelt wird, und URL.getPath () + "#" + URL.getRef () als "path" -Parameter und null als "Fragment" übergeben "Parameter des URI-Klasse-7-Parameterkonstruktors. Standardmäßig wird die Zeichenfolge nach # als Referenz (oder Anker) behandelt.
Gouessej
49

Eine Lösung, die ich entwickelt habe und die viel stabiler ist als jede andere:

public class URLParamEncoder {

    public static String encode(String input) {
        StringBuilder resultStr = new StringBuilder();
        for (char ch : input.toCharArray()) {
            if (isUnsafe(ch)) {
                resultStr.append('%');
                resultStr.append(toHex(ch / 16));
                resultStr.append(toHex(ch % 16));
            } else {
                resultStr.append(ch);
            }
        }
        return resultStr.toString();
    }

    private static char toHex(int ch) {
        return (char) (ch < 10 ? '0' + ch : 'A' + ch - 10);
    }

    private static boolean isUnsafe(char ch) {
        if (ch > 128 || ch < 0)
            return true;
        return " %$&+,/:;=?@<>#%".indexOf(ch) >= 0;
    }

}
fmucar
quelle
3
Dazu müssen Sie auch die URL in Teile zerlegen. Ein Computer kann nicht wissen, welcher Teil der URL zu codieren ist. Siehe meine obige Bearbeitung
fmucar
4
@fmucar Danke für diesen Code! Es ist zu beachten, dass dies nicht UTF-8 ist. Um UTF-8 zu erhalten, verarbeiten Sie die Eingabe einfach mit String utf8Input = new String(Charset.forName("UTF-8").encode(input).array());(von hier übernommen )
letmaik
1
Diese Lösung codiert tatsächlich auch den Teil "http: //" in "http% 3A% 2F% 2F", was die erste Frage zu vermeiden versuchte.
Benjamin Piette
2
Sie übergeben nur das, was Sie zum Codieren benötigen, nicht die gesamte URL. Es gibt keine Möglichkeit, eine ganze URL-Zeichenfolge zu übergeben und eine korrekte Codierung zu erwarten. In allen Fällen müssen Sie die URL in ihre logischen Teile zerlegen.
Fmucar
2
Ich hatte Probleme mit dieser Antwort, da sie keine unsicheren Zeichen in UTF-8 codiert. Dies kann jedoch von der Peer-Anwendung abhängen.
Tarnschaf
36

Wenn Sie eine URL haben, können Sie url.toString () an diese Methode übergeben. Erste Dekodierung, um eine doppelte Codierung zu vermeiden (z. B. ergibt die Codierung eines Leerzeichens% 20 und die Codierung eines Prozentzeichens% 25, ​​sodass die doppelte Codierung aus einem Leerzeichen% 2520 macht). Verwenden Sie dann den URI wie oben erläutert und fügen Sie alle Teile der URL hinzu (damit Sie die Abfrageparameter nicht löschen).

public URL convertToURLEscapingIllegalCharacters(String string){
    try {
        String decodedURL = URLDecoder.decode(string, "UTF-8");
        URL url = new URL(decodedURL);
        URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef()); 
        return uri.toURL(); 
    } catch (Exception ex) {
        ex.printStackTrace();
        return null;
    }
}
Scott Izu
quelle
1
URLDecoder.decode (Zeichenfolge, "UTF-8") schlägt mit einer IllegalArgumentException fehl, wenn Sie die Zeichenfolge als " google.co.in/search?q=123%!123 " übergeben. Dies ist eine gültige URL. Ich denke, diese API funktioniert nicht, wenn% anstelle des Codierungszeichens als Daten verwendet wird.
MediumOne
26

Ja, die URL-Codierung wird diese Zeichenfolge so codieren, dass sie in einer URL ordnungsgemäß an ein endgültiges Ziel übergeben wird. Zum Beispiel könnten Sie http://stackoverflow.com?url=http://yyy.com nicht haben . Die Url-Codierung des Parameters würde diesen Parameterwert korrigieren.

Ich habe also zwei Möglichkeiten für Sie:

  1. Haben Sie Zugriff auf den von der Domäne getrennten Pfad? In diesem Fall können Sie den Pfad möglicherweise einfach per Url-Code codieren. Ist dies jedoch nicht der Fall, ist Option 2 möglicherweise für Sie geeignet.

  2. Holen Sie sich commons-httpclient-3.1. Dies hat eine Klasse URIUtil:

    System.out.println (URIUtil.encodePath (" http://example.com/x y", "ISO-8859-1"));

Dadurch wird genau das ausgegeben, wonach Sie suchen, da nur der Pfadteil des URI codiert wird.

Zu Ihrer Information, Sie benötigen Commons-Codec und Commons-Logging, damit diese Methode zur Laufzeit funktioniert.

Nathan Feger
quelle
Sidenote Apache Commons hat anscheinend die Verwaltung von URIUtil in 4.x-Zweigen eingestellt und empfiehlt, stattdessen die URI-Klasse von JDK zu verwenden. Bedeutet nur, dass Sie die Saite selbst aufbrechen müssen.
Nicholi
2) Genau wird es auch hier vorgeschlagen stackoverflow.com/questions/5330104/… Ich habe auch URIUtilLösung verwendet
Zu Kra
11

Nitpicking: Eine Zeichenfolge, die per Definition ein Leerzeichen enthält, ist keine URI. Sie suchen also nach Code, der das in Abschnitt 2.1 von RFC 3986 definierte URI-Escape implementiert .

Julian Reschke
quelle
Wir brauchen das "Wie" in den Antworten, nicht das "Was".
Shinzou
11

Leider org.apache.commons.httpclient.util.URIUtilist veraltet und die replacement org.apache.commons.codec.net.URLCodecCodierung für Formularbeiträge geeignet, nicht in tatsächlichen URLs. Also musste ich meine eigene Funktion schreiben, die eine einzelne Komponente ausführt (nicht geeignet für ganze Abfragezeichenfolgen mit? 'Und &' s)

public static String encodeURLComponent(final String s)
{
  if (s == null)
  {
    return "";
  }

  final StringBuilder sb = new StringBuilder();

  try
  {
    for (int i = 0; i < s.length(); i++)
    {
      final char c = s.charAt(i);

      if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
          ((c >= '0') && (c <= '9')) ||
          (c == '-') ||  (c == '.')  || (c == '_') || (c == '~'))
      {
        sb.append(c);
      }
      else
      {
        final byte[] bytes = ("" + c).getBytes("UTF-8");

        for (byte b : bytes)
        {
          sb.append('%');

          int upper = (((int) b) >> 4) & 0xf;
          sb.append(Integer.toHexString(upper).toUpperCase(Locale.US));

          int lower = ((int) b) & 0xf;
          sb.append(Integer.toHexString(lower).toUpperCase(Locale.US));
        }
      }
    }

    return sb.toString();
  }
  catch (UnsupportedEncodingException uee)
  {
    throw new RuntimeException("UTF-8 unsupported!?", uee);
  }
}
Jeff Tsay
quelle
Komm schon, es muss eine Bibliothek geben, die das macht.
Shinzou
9

URLEncoding kann HTTP-URLs problemlos codieren, wie Sie leider festgestellt haben. Die von Ihnen übergebene Zeichenfolge " http://search.barnesandnoble.com/booksearch/first book.pdf" wurde korrekt und vollständig in eine URL-codierte Form codiert. Sie könnten die gesamte lange Zeichenfolge von Gobbledigook übergeben, die Sie als Parameter in einer URL erhalten haben, und sie könnte genau in die Zeichenfolge zurückcodiert werden, die Sie übergeben haben.

Es hört sich so an, als ob Sie etwas anderes tun möchten, als die gesamte URL als Parameter zu übergeben. Soweit ich weiß, versuchen Sie, eine Such-URL zu erstellen, die wie folgt aussieht: " http://search.barnesandnoble.com/booksearch/whateverTheUserPassesIn ". Das einzige, was Sie codieren müssen, ist das Bit "WhateverTheUserPassesIn". Vielleicht müssen Sie also nur Folgendes tun:

String url = "http://search.barnesandnoble.com/booksearch/" + 
       URLEncoder.encode(userInput,"UTF-8");

Das sollte etwas ziemlich Gültigeres für Sie hervorbringen.

Brandon Yarbrough
quelle
17
Das würde die Leerzeichen in userInput durch "+" ersetzen. Das Poster muss durch "% 20" ersetzt werden.
Vocaro
@vocaro: das ist ein sehr guter Punkt. URLEncoder wird maskiert, als wären die Argumente Abfrageparameter, nicht wie der Rest der URL.
Brandon Yarbrough
9

Wenn jemand seinem Projekt keine Abhängigkeit hinzufügen möchte, können diese Funktionen hilfreich sein.

Wir übergeben hier den 'Pfad'-Teil unserer URL. Sie möchten wahrscheinlich nicht die vollständige URL als Parameter übergeben (Abfragezeichenfolgen benötigen unterschiedliche Escapezeichen usw.).

/**
 * Percent-encodes a string so it's suitable for use in a URL Path (not a query string / form encode, which uses + for spaces, etc)
 */
public static String percentEncode(String encodeMe) {
    if (encodeMe == null) {
        return "";
    }
    String encoded = encodeMe.replace("%", "%25");
    encoded = encoded.replace(" ", "%20");
    encoded = encoded.replace("!", "%21");
    encoded = encoded.replace("#", "%23");
    encoded = encoded.replace("$", "%24");
    encoded = encoded.replace("&", "%26");
    encoded = encoded.replace("'", "%27");
    encoded = encoded.replace("(", "%28");
    encoded = encoded.replace(")", "%29");
    encoded = encoded.replace("*", "%2A");
    encoded = encoded.replace("+", "%2B");
    encoded = encoded.replace(",", "%2C");
    encoded = encoded.replace("/", "%2F");
    encoded = encoded.replace(":", "%3A");
    encoded = encoded.replace(";", "%3B");
    encoded = encoded.replace("=", "%3D");
    encoded = encoded.replace("?", "%3F");
    encoded = encoded.replace("@", "%40");
    encoded = encoded.replace("[", "%5B");
    encoded = encoded.replace("]", "%5D");
    return encoded;
}

/**
 * Percent-decodes a string, such as used in a URL Path (not a query string / form encode, which uses + for spaces, etc)
 */
public static String percentDecode(String encodeMe) {
    if (encodeMe == null) {
        return "";
    }
    String decoded = encodeMe.replace("%21", "!");
    decoded = decoded.replace("%20", " ");
    decoded = decoded.replace("%23", "#");
    decoded = decoded.replace("%24", "$");
    decoded = decoded.replace("%26", "&");
    decoded = decoded.replace("%27", "'");
    decoded = decoded.replace("%28", "(");
    decoded = decoded.replace("%29", ")");
    decoded = decoded.replace("%2A", "*");
    decoded = decoded.replace("%2B", "+");
    decoded = decoded.replace("%2C", ",");
    decoded = decoded.replace("%2F", "/");
    decoded = decoded.replace("%3A", ":");
    decoded = decoded.replace("%3B", ";");
    decoded = decoded.replace("%3D", "=");
    decoded = decoded.replace("%3F", "?");
    decoded = decoded.replace("%40", "@");
    decoded = decoded.replace("%5B", "[");
    decoded = decoded.replace("%5D", "]");
    decoded = decoded.replace("%25", "%");
    return decoded;
}

Und Tests:

@Test
public void testPercentEncode_Decode() {
    assertEquals("", percentDecode(percentEncode(null)));
    assertEquals("", percentDecode(percentEncode("")));

    assertEquals("!", percentDecode(percentEncode("!")));
    assertEquals("#", percentDecode(percentEncode("#")));
    assertEquals("$", percentDecode(percentEncode("$")));
    assertEquals("@", percentDecode(percentEncode("@")));
    assertEquals("&", percentDecode(percentEncode("&")));
    assertEquals("'", percentDecode(percentEncode("'")));
    assertEquals("(", percentDecode(percentEncode("(")));
    assertEquals(")", percentDecode(percentEncode(")")));
    assertEquals("*", percentDecode(percentEncode("*")));
    assertEquals("+", percentDecode(percentEncode("+")));
    assertEquals(",", percentDecode(percentEncode(",")));
    assertEquals("/", percentDecode(percentEncode("/")));
    assertEquals(":", percentDecode(percentEncode(":")));
    assertEquals(";", percentDecode(percentEncode(";")));

    assertEquals("=", percentDecode(percentEncode("=")));
    assertEquals("?", percentDecode(percentEncode("?")));
    assertEquals("@", percentDecode(percentEncode("@")));
    assertEquals("[", percentDecode(percentEncode("[")));
    assertEquals("]", percentDecode(percentEncode("]")));
    assertEquals(" ", percentDecode(percentEncode(" ")));

    // Get a little complex
    assertEquals("[]]", percentDecode(percentEncode("[]]")));
    assertEquals("a=d%*", percentDecode(percentEncode("a=d%*")));
    assertEquals(")  (", percentDecode(percentEncode(")  (")));
    assertEquals("%21%20%2A%20%27%20%28%20%25%20%29%20%3B%20%3A%20%40%20%26%20%3D%20%2B%20%24%20%2C%20%2F%20%3F%20%23%20%5B%20%5D%20%25",
                    percentEncode("! * ' ( % ) ; : @ & = + $ , / ? # [ ] %"));
    assertEquals("! * ' ( % ) ; : @ & = + $ , / ? # [ ] %", percentDecode(
                    "%21%20%2A%20%27%20%28%20%25%20%29%20%3B%20%3A%20%40%20%26%20%3D%20%2B%20%24%20%2C%20%2F%20%3F%20%23%20%5B%20%5D%20%25"));

    assertEquals("%23456", percentDecode(percentEncode("%23456")));

}
Cuga
quelle
Vielen Dank dafür, aber was muss ich tun, um ein Leerzeichen zu codieren -> verwenden Sie stattdessen% 20 gemäß Ihrem Beispiel?
N00b Pr0grammer
Aktualisiert, um Leerzeichen als% 20
Cuga
7

Es gibt immer noch ein Problem, wenn Ihre URL ein verschlüsseltes "/" (% 2F) enthält.

In RFC 3986 - Abschnitt 2.2 heißt es: "Wenn Daten für eine URI-Komponente mit dem Zweck eines reservierten Zeichens als Trennzeichen in Konflikt stehen würden, müssen die widersprüchlichen Daten vor der Bildung des URI prozentual codiert werden." (RFC 3986 - Abschnitt 2.2)

Es gibt jedoch ein Problem mit Tomcat:

http://tomcat.apache.org/security-6.html - In Apache Tomcat 6.0.10 behoben

wichtig: Verzeichnisdurchquerung CVE-2007-0450

Tomcat erlaubt '\', '% 2F' und '% 5C' [...].

Die folgenden Java-Systemeigenschaften wurden zu Tomcat hinzugefügt, um eine zusätzliche Kontrolle über die Behandlung von Pfadbegrenzern in URLs zu ermöglichen (beide Optionen sind standardmäßig false):

  • org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: true | false
  • org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH: true | false

Da nicht garantiert werden kann, dass alle URLs von Tomcat wie auf Proxyservern verarbeitet werden, sollte Tomcat immer so geschützt werden, als ob kein Proxy verwendet würde, der den Kontextzugriff einschränkt.

Auswirkungen: 6.0.0-6.0.9

Wenn Sie also eine URL mit dem Zeichen% 2F haben, gibt Tomcat Folgendes zurück: "400 Ungültiger URI: noSlash"

Sie können den Bugfix im Tomcat-Startskript umschalten:

set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%   -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true 
Simonox
quelle
7

Ich habe die vorherigen Antworten gelesen, um meine eigene Methode zu schreiben, da ich mit der Lösung der vorherigen Antworten nicht richtig arbeiten konnte. Es sieht gut für mich aus. Wenn Sie jedoch eine URL finden, die damit nicht funktioniert, lassen Sie es mich bitte wissen.

public static URL convertToURLEscapingIllegalCharacters(String toEscape) throws MalformedURLException, URISyntaxException {
            URL url = new URL(toEscape);
            URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
            //if a % is included in the toEscape string, it will be re-encoded to %25 and we don't want re-encoding, just encoding
            return new URL(uri.toString().replace("%25", "%"));
}
Emilien Brigand
quelle
4

Ich stimme Matt zu. In der Tat habe ich es in Tutorials noch nie gut erklärt gesehen, aber eine Frage ist, wie der URL-Pfad codiert wird, und eine ganz andere ist, wie die Parameter codiert werden, die an die URL angehängt werden (der Abfrageteil hinter dem "? "Symbol). Sie verwenden eine ähnliche Codierung, jedoch nicht dieselbe.

Speziell für die Codierung des Leerzeichens. Der URL-Pfad muss als% 20 codiert sein, während der Abfrageteil% 20 und auch das "+" - Zeichen zulässt. Die beste Idee ist, es selbst mit einem Webbrowser gegen unseren Webserver zu testen.

Für beide Fälle, ich IMMER würde kodieren Komponente für Komponente , nie die ganze Reihe. In der Tat erlaubt URLEncoder dies für den Abfrageteil. Für den Pfadteil können Sie den Klassen-URI verwenden, obwohl in diesem Fall die gesamte Zeichenfolge und nicht eine einzelne Komponente abgefragt wird.

Ich glaube jedenfalls, dass der beste Weg, um diese Probleme zu vermeiden, die Verwendung eines persönlichen, nicht konfliktreichen Designs ist. Wie? Zum Beispiel würde ich niemals Verzeichnisse oder Parameter mit anderen Zeichen als aZ, AZ, 0-9 und _ benennen. Auf diese Weise muss nur der Wert jedes Parameters codiert werden, da er möglicherweise aus einer Benutzereingabe stammt und die verwendeten Zeichen unbekannt sind.

Negora
quelle
2
Beispielcode unter Verwendung der URL in der Frage wäre eine gute Sache, um Ihre Antwort
Martin Serrano
3

Vielleicht können Sie UriUtils in org.springframework.web.util ausprobieren

UriUtils.encodeUri(input, "UTF-8")
micahli123
quelle
3

Sie können auch GUAVAEscaper verwenden und verwenden: UrlEscapers.urlFragmentEscaper().escape(relativePath)

Zu Kra
quelle
2

Zusätzlich zur Antwort von Carlos Heuberger: Wenn eine andere als die Standardeinstellung (80) benötigt wird, sollte der Konstruktor 7 param verwendet werden:

URI uri = new URI(
        "http",
        null, // this is for userInfo
        "www.google.com",
        8080, // port number as int
        "/ig/api",
        "weather=São Paulo",
        null);
String request = uri.toASCIIString();
Martin Dimitrov
quelle
2

Ich habe den obigen Inhalt genommen und ein wenig geändert. Ich mag zuerst positive Logik, und ich dachte, ein HashSet bietet möglicherweise eine bessere Leistung als einige andere Optionen, wie das Durchsuchen eines Strings. Ich bin mir zwar nicht sicher, ob sich die Autoboxing-Strafe lohnt, aber wenn der Compiler für ASCII-Zeichen optimiert, sind die Kosten für das Boxen niedrig.

/***
 * Replaces any character not specifically unreserved to an equivalent 
 * percent sequence.
 * @param s
 * @return
 */
public static String encodeURIcomponent(String s)
{
    StringBuilder o = new StringBuilder();
    for (char ch : s.toCharArray()) {
        if (isSafe(ch)) {
            o.append(ch);
        }
        else {
            o.append('%');
            o.append(toHex(ch / 16));
            o.append(toHex(ch % 16));
        }
    }
    return o.toString();
}

private static char toHex(int ch)
{
    return (char)(ch < 10 ? '0' + ch : 'A' + ch - 10);
}

// https://tools.ietf.org/html/rfc3986#section-2.3
public static final HashSet<Character> UnreservedChars = new HashSet<Character>(Arrays.asList(
        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
        'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
        '0','1','2','3','4','5','6','7','8','9',
        '-','_','.','~'));
public static boolean isSafe(char ch)
{
    return UnreservedChars.contains(ch);
}
ChrisG65
quelle
1

Verwenden Sie die folgende Standard-Java-Lösung (besteht ungefähr 100 der von Web Plattform Tests bereitgestellten Testfälle ):

0. Testen Sie, ob die URL bereits codiert ist .

1. Teilen Sie die URL in Strukturteile auf. Verwenden java.net.URL Sie dafür.

2. Codieren Sie jedes Bauteil richtig!

3. Verwenden Sie Punycode, um den Hostnamen IDN.toASCII(putDomainNameHere)zu codieren!

4. Verwenden Sie java.net.URI.toASCIIString(), um NFC-codierten Unicode in Prozent zu codieren - (besser wäre NFKC!).

Weitere Informationen finden Sie hier: https://stackoverflow.com/a/49796882/1485527

jschnasse
quelle
0

Ich habe ein neues Projekt erstellt, um beim Erstellen von HTTP-URLs zu helfen. Die Bibliothek codiert Pfadsegmente und Abfrageparameter automatisch per URL.

Sie können die Quelle anzeigen und eine Binärdatei unter https://github.com/Widen/urlbuilder herunterladen

Die Beispiel-URL in dieser Frage:

new UrlBuilder("search.barnesandnoble.com", "booksearch/first book.pdf").toString()

produziert

http://search.barnesandnoble.com/booksearch/first%20book.pdf

Uriah Carpenter
quelle
0

Ich hatte das gleiche Problem. Gelöst durch Unsing:

android.net.Uri.encode(urlString, ":/");

Es codiert die Zeichenfolge, überspringt jedoch ":" und "/".

Richard R.
quelle
0

ich benutze das

org.apache.commons.text.StringEscapeUtils.escapeHtml4("my text % & < >");

Fügen Sie diese Abhängigkeit hinzu

 <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>1.8</version>
    </dependency>
Entwickler learn999
quelle
-2

Ich entwickle eine Bibliothek, die diesem Zweck dient: Galimaten . Es analysiert URLs genauso wie Webbrowser. Das heißt, wenn eine URL in einem Browser funktioniert, wird sie von Galimaten korrekt analysiert .

In diesem Fall:

// Parse
io.mola.galimatias.URL.parse(
    "http://search.barnesandnoble.com/booksearch/first book.pdf"
).toString()

Wird Ihnen geben : http://search.barnesandnoble.com/booksearch/first%20book.pdf. Natürlich ist dies der einfachste Fall, aber es wird mit allem funktionieren, weit darüber hinaus java.net.URI.

Sie können es unter folgender Adresse überprüfen: https://github.com/smola/galimatias

smola
quelle
-3

Sie können eine solche Funktion verwenden. Vervollständigen Sie es und passen Sie es an Ihre Bedürfnisse an:

/**
     * Encode URL (except :, /, ?, &, =, ... characters)
     * @param url to encode
     * @param encodingCharset url encoding charset
     * @return encoded URL
     * @throws UnsupportedEncodingException
     */
    public static String encodeUrl (String url, String encodingCharset) throws UnsupportedEncodingException{
            return new URLCodec().encode(url, encodingCharset).replace("%3A", ":").replace("%2F", "/").replace("%3F", "?").replace("%3D", "=").replace("%26", "&");
    }

Anwendungsbeispiel:

String urlToEncode = ""http://www.growup.com/folder/intérieur-à_vendre?o=4";
Utils.encodeUrl (urlToEncode , "UTF-8")

Das Ergebnis ist: http://www.growup.com/folder/int%C3%A9rieur-%C3%A0_vendre?o=4

Salim Hamidi
quelle
1
Diese Antwort ist ohne URLCodec unvollständig.
Marquis von Lorne
upvote für .replace () Verkettung, es ist nicht ideal, aber es reicht für grundlegende Ad-hoc-Anwendungsfälle
svarog
-5

String url = "" http://search.barnesandnoble.com/booksearch/ ;

Dies wird wahrscheinlich konstant sein und nur der Dateiname ändert sich dyamisch, also erhalte den Dateinamen

String Dateiname; // den Dateinamen abrufen

String urlEnc = url + fileName.replace ("", "% 20");

Raja
quelle
2
Was ist mit all den anderen illegalen Charakteren?
Marquis von Lorne
-7

Wie wäre es mit:

public String UrlEncode (String in_) {

String retVal = "";

try {
    retVal = URLEncoder.encode(in_, "UTF8");
} catch (UnsupportedEncodingException ex) {
    Log.get().exception(Log.Level.Error, "urlEncode ", ex);
}

return retVal;

}}

MichaelICE
quelle
URLEncoder kann nicht verwendet werden, um ivalide URL-Zeichen zu umgehen. Nur um Formulare zu codieren.
Archer