Kodowanie adresu URL HTTP w Javie

366

Moja samodzielna aplikacja Java pobiera adres URL (który wskazuje na plik) od użytkownika i muszę go nacisnąć i pobrać. Problem, przed którym stoję, polega na tym, że nie jestem w stanie poprawnie zakodować adresu URL HTTP ...

Przykład:

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

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

zwraca mi:

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

Ale chcę tego

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

(spacja zastąpiona przez% 20)

URLEncoderWydaje mi się, że nie jest przeznaczony do kodowania adresów URL HTTP ... JavaDoc mówi „Klasa użytkowa do kodowania formularzy HTML” ... Czy jest jakiś inny sposób to zrobić?

suDocker
źródło
1
Zobacz także stackoverflow.com/questions/10786042/…
Raedwald
Zachowanie jest całkowicie poprawne. Kodowanie URL polega na przekształceniu czegoś w ciąg znaków, który można bezpiecznie przekazać jako parametr adresu URL i nie jest on wcale interpretowany jako adres URL. Podczas gdy chcesz, aby po prostu przekonwertować jedną małą część adresu URL.
Stephen Holt

Odpowiedzi:

303

Java.net.URI klasa może pomóc; w dokumentacji znalezionego adresu URL

Zauważ, że klasa URI w pewnych okolicznościach dokonuje zmiany znaczenia swoich pól składowych. Zalecanym sposobem zarządzania kodowaniem i dekodowaniem adresów URL jest użycie identyfikatora URI

Użyj jednego z konstruktorów z więcej niż jednym argumentem, na przykład:

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

(konstruktor URI z jednym argumentem NIE ucieka przed niedozwolonymi znakami)


Tylko niedozwolone znaki są usuwane przez powyższy kod - NIE ucieka to znaków spoza ASCII (patrz komentarz fatih). Metoda może być stosowana, aby uzyskać ciąg znaków tylko z US-ASCII:
toASCIIString

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

W przypadku adresu URL z zapytaniem podobnym http://www.google.com/ig/api?weather=São Pauloużyj 5-parametrowej wersji konstruktora:

URI uri = new URI(
        "http", 
        "www.google.com", 
        "/ig/api",
        "weather=São Paulo",
        null);
String request = uri.toASCIIString();
użytkownik85421
źródło
13
Uwaga: wspomniana tutaj klasa URI pochodzi z „org.apache.commons.httpclient.URI”, nie „java.net”, „java.net” nie przyjmuje niedozwolonych znaków, chyba że użyjesz konstruktory, które budują adres URL z jego składników, jak w sposób opisany w komentarzu Matta poniżej
Mohamed Faramawi
7
@Mohamed: klasa, o której wspomniałem i której użyłem do testowania, to java.net.URI : działała idealnie (Java 1.6). Wspomniałbym w pełni kwalifikowaną nazwę klasy, gdyby nie była standardową nazwą Java, a link wskazuje na dokumentację java.net.URI. I dzięki komentarzowi Sudhakara rozwiązał problem bez uwzględnienia „wspólnych bibliotek”!
user85421
1
URI uri = nowy URI („http”, „search.barnesandnoble.com”, „/ booksearch / é”, null); Czy nie ma poprawnego ucieczki z tą próbką? Trzeba było tego uniknąć przy pomocy% escapes
fmucar,
@fatih - zgadza się, dzięki! Zwykle nie powinno to stanowić problemu, ale istnieje proste rozwiązanie - prawie takie samo, jak pisałem wcześniej. Zobacz 2. edycję.
user85421,
@Carlos Thx do edycji. Teraz ucieka, ale nie poprawia ucieczki. Powinien być dodawany% do HEX wartości char dla parametrów ścieżki, co oznacza, że ​​é char należy przekonwertować na% e9
fmucar
91

Należy pamiętać, że większość powyższych odpowiedzi jest NIEPRAWIDŁOWA.

URLEncoderKlasy, mimo to nazwisko, a nie to, co musi być tutaj. Szkoda, że ​​Sun nazwał tę klasę tak irytująco. URLEncodersłuży do przekazywania danych jako parametrów, a nie do kodowania samego adresu URL.

Innymi słowy, "http://search.barnesandnoble.com/booksearch/first book.pdf"to adres URL. Parametry to na przykład "http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that". Parametry są do tego użyte URLEncoder.

Poniższe dwa przykłady podkreślają różnice między nimi.

Poniżej przedstawiono nieprawidłowe parametry, zgodnie ze standardem HTTP. Zwróć uwagę, że znaki ampersand (&) i plus (+) są niepoprawnie kodowane.

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:)

Poniższe spowoduje wygenerowanie poprawnych parametrów z poprawnie zakodowanym zapytaniem. Zwróć uwagę na spacje, znaki handlowe i znaki plus.

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
źródło
2
Zgadza się, konstruktor URI już koduje kwerendę, zgodnie z dokumentacją 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 Odpowiedź jest poprawna, ale używa ciągu zapytania w niecodzienny sposób; bardziej normalnym przykładem może być query = URLEncoder.encode(key) + "=" + URLEncoder.encode(value). Dokumenty mówią tylko, że „cytowany jest dowolny znak, który nie jest legalnym znakiem URI”.
tc.
1
Zgadzam się z Mattem tutaj. Jeśli wpiszesz ten adres URL: „ google.com/help/me/book name + me /? MY CRZY QUERY! + & + :)” w przeglądarce, automatycznie koduje spacje, ale „&” jest używane jako wartość zapytania separator i „+” zostaną utracone.
arcot
80

Dodam tutaj jedną sugestię skierowaną do użytkowników Androida. Możesz to zrobić, co pozwala uniknąć pobierania zewnętrznych bibliotek. Ponadto wszystkie rozwiązania wyszukiwania / zamiany znaków sugerowane w niektórych powyższych odpowiedziach są niebezpieczne i należy ich unikać.

Wypróbuj to:

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

Widać, że w tym konkretnym adresie URL muszę zakodować te spacje, aby móc użyć go do żądania.

Wykorzystuje to kilka funkcji dostępnych w klasach Androida. Po pierwsze, klasa URL może rozbić adres URL na odpowiednie komponenty, więc nie trzeba wykonywać żadnych operacji wyszukiwania / zamiany łańcucha. Po drugie, w tym podejściu wykorzystuje się właściwość klasy URI polegającą na prawidłowym ucieczce komponentów podczas konstruowania identyfikatora URI za pomocą komponentów, a nie pojedynczego łańcucha.

Piękno tego podejścia polega na tym, że możesz wziąć dowolny prawidłowy ciąg adresu URL i sprawić, aby działał bez potrzeby specjalnej wiedzy na jego temat.

Craig B.
źródło
3
Ładne podejście, ale chciałbym zauważyć, że ten kod nie zapobiega podwójnemu kodowaniu , np.% 20 został zakodowany w% 2520. Odpowiedź Scotta nie cierpi z tego powodu.
nattster
2
Nie da sobie rady #.
Alston,
Lub jeśli chcesz po prostu cytować ścieżkę: nowy identyfikator URI (null, null, "/ path with spaces", null, null) .toString ()
user1050755
1
@Stallman Jeśli nazwa pliku zawiera #, klasa URL umieści go w „ref” (odpowiednik „fragmentu” w klasie URI). Możesz wykryć, czy URL.getRef () zwraca coś, co może być traktowane jako część ścieżki, i przekazać URL.getPath () + „#” + URL.getRef () jako parametr „path”, a null jako „fragment” "parametr konstruktora parametrów klasy 7 URI. Domyślnie ciąg po # jest traktowany jako odwołanie (lub kotwica).
gouessej
49

rozwiązanie, które opracowałem i jest znacznie bardziej stabilne niż jakiekolwiek inne:

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
źródło
3
wymaga to również podzielenia adresu URL na części. Komputer nie może wiedzieć, którą część adresu URL należy zakodować. Zobacz moją powyższą edycję
fmucar
4
@fmucar Dzięki za ten kawałek kodu! Należy zauważyć, że nie jest to UTF-8. Aby uzyskać UTF-8, wystarczy wstępnie przetworzyć dane wejściowe za pomocą String utf8Input = new String(Charset.forName("UTF-8").encode(input).array());(pobranego stąd )
letmaik,
1
To rozwiązanie faktycznie zakoduje również część „http: //” w „http% 3A% 2F% 2F”, czego próbowało uniknąć początkowe pytanie.
Benjamin Piette,
2
Przekazujesz tylko to, co musisz zakodować, a nie cały adres URL. Nie ma sposobu, aby przekazać cały ciąg adresu URL i oczekiwać poprawnego kodowania. We wszystkich przypadkach musisz rozbić adres URL na logiczne części.
fmucar
2
Miałem problemy z tą odpowiedzią, ponieważ nie koduje ona niebezpiecznych znaków do UTF-8 .. może jednak zależeć od aplikacji równorzędnej.
Tarnschaf
36

Jeśli masz adres URL, możesz przekazać metodę url.toString () do tej metody. Najpierw dekoduj, aby uniknąć podwójnego kodowania (na przykład kodowanie spacji powoduje% 20, a kodowanie znaku procentu powoduje% 25, ​​więc podwójne kodowanie zamieni spację w% 2520). Następnie użyj identyfikatora URI, jak wyjaśniono powyżej, dodając wszystkie części adresu URL (aby nie upuścić parametrów zapytania).

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
źródło
1
URLDecoder.decode (ciąg „UTF-8”) nie działa z IllegalArgumentException, gdy przekazujesz ciąg jako „ google.co.in/search?q=123%!123 ”. To jest poprawny adres URL. Myślę, że ten interfejs API nie działa, gdy% jest używane jako dane zamiast znaku kodującego.
MediumOne
26

Tak, kodowanie URL będzie kodować ten ciąg, aby był poprawnie przekazywany w adresie URL do miejsca docelowego. Na przykład nie możesz mieć http://stackoverflow.com?url=http://yyy.com . UrlEncoding parametru naprawi tę wartość parametru.

Mam dla ciebie dwie możliwości:

  1. Czy masz dostęp do ścieżki niezależnej od domeny? Jeśli tak, możesz po prostu UrlEncode ścieżkę. Jeśli jednak tak nie jest, opcja 2 może być dla Ciebie.

  2. Pobierz commons-httpclient-3.1. Ma to klasę URIUtil:

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

Spowoduje to wyświetlenie dokładnie tego, czego szukasz, ponieważ koduje tylko część ścieżki URI.

Do twojej metody będziesz potrzebował kodera-dekodera i logowania do wspólnego, aby ta metoda działała w czasie wykonywania.

Nathan Feger
źródło
Widocznie apache Sidenote przestał utrzymywać URIUtil w gałęziach 4.x, zalecając zamiast tego użycie klasy URI JDK. Oznacza to tylko, że musisz sam zerwać sznurek.
Nicholi
2) Dokładnie jest to również sugerowane tutaj stackoverflow.com/questions/5330104/... Użyłem również URIUtilrozwiązania
To Kra
11

Nitpicking: ciąg znaków z definicji spacją nie jest identyfikatorem URI. Tak więc szukasz kodu, który implementuje ucieczkę URI zdefiniowany w Sekcji 2.1 RFC 3986 .

Julian Reschke
źródło
Potrzebujemy odpowiedzi „jak”, a nie „co”.
shinzou,
11

Niestety org.apache.commons.httpclient.util.URIUtiljest przestarzałe, a replacement org.apache.commons.codec.net.URLCodeckodowanie nadaje się do postów, a nie rzeczywistych adresów URL. Musiałem więc napisać własną funkcję, która wykonuje pojedynczy komponent (nieodpowiedni dla całych ciągów zapytań zawierających? I &)

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
źródło
Chodź, musi to być biblioteka.
shinzou
9

URLEncoding potrafi dobrze kodować adresy URL HTTP, co niestety odkryłeś. Przekazany ciąg „ http://search.barnesandnoble.com/booksearch/first book.pdf” został poprawnie i całkowicie zakodowany w postaci zakodowanej w adresie URL. Możesz przekazać cały długi ciąg gobbledigook, który dostałeś jako parametr w adresie URL, i można go dekodować z powrotem do dokładnie tego, który przekazałeś.

Wygląda na to, że chcesz zrobić coś innego niż przekazywanie całego adresu URL jako parametru. Z tego, co zbieram, próbujesz utworzyć URL wyszukiwania, który wygląda jak „ http://search.barnesandnoble.com/booksearch/whthingTheUserPassesIn ”. Jedyne, co musisz zakodować, to bit „whatTheUserPassesIn”, więc być może wszystko, co musisz zrobić, to coś takiego:

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

To powinno dać ci coś bardziej odpowiedniego dla ciebie.

Brandon Yarbrough
źródło
17
Zastąpiłoby to spacje w userInput znakiem „+”. Plakat wymaga ich zastąpienia przez „% 20”.
vocaro
@vocaro: to bardzo dobra uwaga. URLEncoder ucieka, tak jak argumenty są parametrami zapytania, a nie jak reszta adresu URL.
Brandon Yarbrough
9

Jeśli ktoś nie chce dodawać zależności do swojego projektu, te funkcje mogą być pomocne.

Przekazujemy tutaj część „ścieżki” naszego adresu URL. Prawdopodobnie nie chcesz przekazać pełnego adresu URL jako parametru (ciągi zapytania wymagają różnych znaków zmiany znaczenia itp.).

/**
 * 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;
}

I testy:

@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
źródło
Dzięki za to, ale co muszę zrobić, aby zakodować spację -> użyj zamiast tego% 20 jak w twoim przykładzie?
N00b Pr0grammer,
Zaktualizowano w celu uwzględnienia spacji jako% 20
Cuga,
7

Nadal występuje problem, jeśli masz zakodowany „/” (% 2F) w swoim adresie URL.

RFC 3986 - Sekcja 2.2 mówi: „Jeśli dane dla komponentu URI byłyby sprzeczne z celem znaku zarezerwowanego jako separatora, wówczas konfliktowe dane muszą zostać zakodowane procentowo przed utworzeniem URI”. (RFC 3986 - sekcja 2.2)

Ale jest problem z Tomcat:

http://tomcat.apache.org/security-6.html - Naprawiono w Apache Tomcat 6.0.10

ważne: Przejście do katalogu CVE-2007-0450

Tomcat zezwala na „\”, „% 2F” i „% 5C” [...].

Następujące właściwości systemu Java zostały dodane do Tomcat, aby zapewnić dodatkową kontrolę nad obsługą ograniczników ścieżek w adresach URL (obie opcje domyślnie mają wartość false):

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

Ze względu na niemożność zagwarantowania, że ​​wszystkie adresy URL są obsługiwane przez Tomcat, ponieważ znajdują się one na serwerach proxy, Tomcat należy zawsze zabezpieczyć, tak jakby nie zastosowano dostępu kontekstowego ograniczającego proxy.

Wpływa: 6.0.0–6.0.9

Więc jeśli masz adres URL ze znakiem% 2F, Tomcat zwraca: „400 Nieprawidłowy identyfikator URI: noSlash”

Możesz przełączyć poprawkę w skrypcie startowym Tomcat:

set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%   -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true 
simonox
źródło
7

Przeczytałem poprzednie odpowiedzi, aby napisać własną metodę, ponieważ nie mogłem mieć czegoś poprawnie działającego przy użyciu rozwiązania poprzednich odpowiedzi, wygląda to dla mnie dobrze, ale jeśli możesz znaleźć URL, który nie działa z tym, daj mi znać.

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
źródło
4

Zgadzam się z Mattem. Rzeczywiście, nigdy nie widziałem tego dobrze wyjaśnionego w samouczkach, ale jedną kwestią jest to, jak zakodować ścieżkę adresu URL, a zupełnie inną kwestią jest sposób kodowania parametrów, które są dołączane do adresu URL (część zapytania, za „? „symbol). Używają podobnego kodowania, ale nie tego samego.

Specjalnie do kodowania znaku białych znaków. Ścieżka adresu URL wymaga zakodowania jako% 20, podczas gdy część zapytania umożliwia% 20, a także znak „+”. Najlepszym pomysłem jest przetestowanie go na własnym serwerze internetowym za pomocą przeglądarki internetowej.

W obu przypadkach ZAWSZE kodowałbym COMPONENT BY COMPONENT , nigdy cały łańcuch. Rzeczywiście URLEncoder pozwala na to w części zapytania. Do części ścieżki można użyć identyfikatora URI klasy, chociaż w tym przypadku prosi on o cały ciąg, a nie o pojedynczy komponent.

W każdym razie uważam, że najlepszym sposobem na uniknięcie tych problemów jest zastosowanie osobistego, nie powodującego konfliktu projektu. W jaki sposób? Na przykład nigdy nie nazwałbym katalogów ani parametrów przy użyciu znaków innych niż aZ, AZ, 0-9 i _. W ten sposób wystarczy zakodować wartość każdego parametru, ponieważ może on pochodzić z danych wejściowych użytkownika, a użyte znaki są nieznane.

negora
źródło
2
przykładowy kod wykorzystujący adres URL w pytaniu dobrze byłoby podać w swojej odpowiedzi
Martin Serrano,
3

Może może wypróbować UriUtils w org.springframework.web.util

UriUtils.encodeUri(input, "UTF-8")
micahli123
źródło
3

Możesz także użyć GUAVAescapera i ścieżki: UrlEscapers.urlFragmentEscaper().escape(relativePath)

Do Kra
źródło
2

Oprócz odpowiedzi Carlosa Heubergera: jeśli potrzebny jest inny niż domyślny (80), należy użyć konstruktora 7 param:

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
źródło
2

Wziąłem powyższą treść i nieco ją zmieniłem. Najpierw lubię logikę pozytywną i pomyślałem, że zestaw HashSet może dać lepszą wydajność niż niektóre inne opcje, takie jak przeszukiwanie ciągu. Chociaż nie jestem pewien, czy kara z tytułu autoboxowania jest tego warta, ale jeśli kompilator zoptymalizuje się pod znaki ASCII, to koszt boksu będzie niski.

/***
 * 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
źródło
1

Użyj następującego standardowego rozwiązania Java (przechodzi około 100 przypadków testowych dostarczonych przez Web Plattform Tests ):

0. Sprawdź, czy adres URL jest już zakodowany .

1. Podziel adres URL na części strukturalne. Użyj java.net.URL do tego.

2. Zakoduj poprawnie każdą część konstrukcyjną!

3. Użyj IDN.toASCII(putDomainNameHere)aby Punycode zakodować nazwę hosta!

4. Użyj java.net.URI.toASCIIString()do kodowania procentowego, kodowania NFC unicode - (lepiej byłoby NFKC!).

Znajdź więcej tutaj: https://stackoverflow.com/a/49796882/1485527

jschnasse
źródło
0

Stworzyłem nowy projekt, aby pomóc w tworzeniu adresów URL HTTP. Biblioteka automatycznie koduje adresy URL segmentów ścieżki i parametry zapytania.

Możesz wyświetlić źródło i pobrać plik binarny na https://github.com/Widen/urlbuilder

Przykładowy adres URL w tym pytaniu:

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

produkuje

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

Uriah Carpenter
źródło
0

Miałem ten sam problem. Rozwiązano to przez:

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

Koduje ciąg, ale pomija „:” i „/”.

Richard R.
źródło
0

używam tego

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

dodaj tę zależność

 <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>1.8</version>
    </dependency>
programista learn999
źródło
-2

Tworzę bibliotekę, która służy temu celowi: galimatias . Analizuje adres URL w taki sam sposób jak przeglądarki internetowe. Oznacza to, że jeśli URL działa w przeglądarce, zostanie poprawnie przeanalizowany przez galimatias .

W tym przypadku:

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

Daje: http://search.barnesandnoble.com/booksearch/first%20book.pdf. Oczywiście jest to najprostszy przypadek, ale będzie działał z wszystkim, nawet daleko java.net.URI.

Możesz to sprawdzić na stronie : https://github.com/smola/galimatias

smola
źródło
-3

Możesz użyć takiej funkcji. Uzupełnij i zmodyfikuj według swoich potrzeb:

/**
     * 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", "&");
    }

Przykład zastosowania:

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

Wynik jest następujący: http://www.growup.com/folder/int%C3%A9rieur-%C3%A0_vendre?o=4

Salim Hamidi
źródło
1
Ta odpowiedź jest niekompletna bez URLCodec.
Markiz Lorne
upvote dla łańcucha .replace (), nie jest idealny, ale wystarcza do podstawowych przypadków użycia ad-hoc
svarog
-5

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

Myślę, że będzie to stała i tylko nazwa pliku zmienia się drastycznie, więc pobierz nazwę pliku

Nazwa pliku ciągu; // pobierz nazwę pliku

Ciąg urlEnc = url + nazwa_pliku.replace ("", "% 20");

radża
źródło
2
Co z wszystkimi innymi nielegalnymi postaciami?
Markiz Lorne
-7

Co powiesz na:

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
źródło
URLEncoder nie może być użyty do zmiany nieprawidłowych znaków URL. Tylko do kodowania formularzy.
Łucznik