Codifica URL Java dei parametri della stringa di query


710

Di 'che ho un URL

http://example.com/query?q=

e ho inserito una query dall'utente come:

parola casuale £ 500 banca $

Voglio che il risultato sia un URL codificato correttamente:

http://example.com/query?q=random%20word%20%A3500%20bank%20%24

Qual è il modo migliore per raggiungere questo obiettivo? Ho provato a URLEncodercreare oggetti URI / URL, ma nessuno di questi è uscito nel modo giusto.


25
Che cosa intendi con "nessuno di loro viene fuori abbastanza bene"?
Mark Elliot,

2
Ho usato URI.create e ho sostituito gli spazi con + in querystring. Nel sito client è stato convertito + indietro in spazi quando ho selezionato le stringhe di query. Questo ha funzionato per me.
ND27,


Perché ti aspetti che $ sia codificato in percentuale?
jschnasse,

Risposte:


1151

URLEncoderè la strada da percorrere. Devi solo ricordare di codificare solo il nome e / o il valore del singolo parametro della stringa di query, non l'intero URL, di sicuro non il carattere di separazione dei parametri della stringa di query &né il carattere di separazione nome-valore del parametro =.

String q = "random word £500 bank $";
String url = "https://example.com?q=" + URLEncoder.encode(q, StandardCharsets.UTF_8);

Si noti che gli spazi nei parametri della query sono rappresentati +, non %20, che è legittimamente valido. Di %20solito deve essere usato per rappresentare gli spazi nell'URI stesso (la parte prima del carattere separatore della stringa di query URI ?), non nella stringa di query (la parte dopo ?).

Si noti inoltre che esistono tre encode()metodi. Uno senza Charsetsecondo argomento e un altro Stringcome secondo argomento che genera un'eccezione controllata. Quello senza Charsetargomento è deprecato. Non usarlo mai e specificare sempre l' Charsetargomento. Il javadoc raccomanda anche esplicitamente di utilizzare la codifica UTF-8, come richiesto dalla RFC3986 e W3C .

Tutti gli altri caratteri non sono sicuri e vengono prima convertiti in uno o più byte utilizzando uno schema di codifica. Quindi ogni byte è rappresentato dalla stringa di 3 caratteri "% xy", dove xy è la rappresentazione esadecimale a due cifre del byte. Lo schema di codifica consigliato da utilizzare è UTF-8 . Tuttavia, per motivi di compatibilità, se non viene specificata una codifica, viene utilizzata la codifica predefinita della piattaforma.

Guarda anche:


Ci possono essere 2 tipi di parametri nell'URL. Stringa di query (seguita da?) E parametro del percorso (in genere parte dell'URL stesso). Quindi, per quanto riguarda i parametri del percorso. URLEncoder produce + per lo spazio anche per i parametri del percorso. In realtà non gestisce altro che una stringa di query. Inoltre, questo comportamento non è sincronizzato con i server js del nodo. Quindi per me questa classe è uno spreco e non può essere utilizzata se non per scenari molto specifici / speciali.
Sharadendu Sinha,

2
@sharadendusinha: come documentato e risposto, URLEncoderè per le application/x-www-form-urlencodedregole conformi ai parametri di query con codifica URL . I parametri del percorso non rientrano in questa categoria. È invece necessario un codificatore URI.
BalusC,

Come avevo previsto, gli utenti si confondono perché ovviamente il problema è che le persone devono codificare più del semplice valore del parametro. È un caso molto raro che devi solo codificare un valore di parametro. È per questo che ho fornito la mia risposta wiki "confusa" per aiutare persone come @sharadendusinha.
Adam Gent,

1
@WijaySharma: perché anche i caratteri specifici dell'URL verrebbero codificati. Dovresti farlo solo quando vuoi passare l'intero URL come parametro di query di un altro URL.
BalusC

1
"+, non% 20" è quello che dovevo sentire. Grazie mille.
wetjosh

173

Non vorrei usare URLEncoder. Oltre ad essere erroneamente chiamato ( URLEncodernon ha nulla a che fare con gli URL), inefficiente (usa un StringBufferinvece di Builder e fa un paio di altre cose che sono lente) È anche troppo facile da rovinare.

Invece avrei usato URIBuildero Primavera org.springframework.web.util.UriUtils.encodeQueryo Commons ApacheHttpClient . Il motivo è che devi evitare il nome dei parametri della query (ovvero la risposta di BalusC q) in modo diverso rispetto al valore del parametro.

L'unico aspetto negativo di quanto sopra (che ho scoperto dolorosamente) è che gli URL non sono un sottoinsieme vero di URI .

Codice di esempio:

import org.apache.http.client.utils.URIBuilder;

URIBuilder ub = new URIBuilder("http://example.com/query");
ub.addParameter("q", "random word £500 bank \$");
String url = ub.toString();

// Result: http://example.com/query?q=random+word+%C2%A3500+bank+%24

Dal momento che sto solo collegando ad altre risposte, l'ho contrassegnato come wiki della comunità. Sentiti libero di modificare.


2
Perché non ha nulla a che fare con gli URL?
Luis set

15
@Luis: URLEncodercome dice javadoc, l'intenzione di codificare i parametri della stringa di query è conforme a application/x-www-form-urlencodedquanto descritto nelle specifiche HTML: w3.org/TR/html4/interact/… . Alcuni utenti infatti lo confondono / abusano per la codifica di interi URI, come apparentemente ha fatto l'attuale risponditore.
BalusC

8
@LuisSep in breve URLEncoder serve per la codifica per l'invio del modulo. Non è per scappare. Non è esattamente la stessa evasione che useresti per creare URL da inserire nella tua pagina web, ma sembra essere abbastanza simile da abusarne. L'unica volta che dovresti usare URLEncoder è se stai scrivendo un client HTTP (e anche allora ci sono opzioni di gran lunga superiori per la codifica).
Adam Gent,

1
@BalusC " Alcuni utenti infatti lo confondono / abusano per la codifica di interi URI, come apparentemente ha fatto l'attuale risponditore. ". Hai pensato male. Non ho mai detto di aver rovinato tutto. Ho appena visto altri che l'hanno fatto, chi sono i bug che devo correggere. La parte che ho rovinato è che la classe URL Java accetterà parentesi senza escape ma non la classe URI. Ci sono molti modi per rovinare la costruzione di URL e non tutti sono brillanti come te. Direi che la maggior parte degli utenti che stanno cercando SO per URLEncoding probabilmente stanno scappando dall'URI "gli utenti confondono / abusano ".
Adam Gent,

1
La domanda non riguardava questo, ma la tua risposta lo implica.
BalusC,

99

Devi prima creare un URI come:

String urlStr = "http://www.example.com/CEREC® Materials & Accessories/IPS Empress® CAD.pdf"
URL url= new URL(urlStr);
URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());

Quindi converti quell'Uri in stringa ASCII:

urlStr=uri.toASCIIString();

Ora la tua stringa url è completamente codificata prima abbiamo fatto una semplice codifica url e poi l'abbiamo convertita in stringa ASCII per assicurarci che nessun carattere al di fuori di US-ASCII rimanga nella stringa. Questo è esattamente come fanno i browser.


7
Grazie! È stupido che la tua soluzione funzioni, ma non integrata URL.toURI().
user11153

2
Sfortunatamente questo non sembra funzionare con "file: ///" (es: "file: /// some / directory / un file contenente spazi.html"); bombarda con MalformedURLException in "nuovo URL ()"; qualche idea su come risolvere questo?
ZioByte,

Devi fare qualcosa del genere: String urlStr = " some / directory / un file contenente spazi.html"; URL url = nuovo URL (urlStr); URI uri = new URI (url.getProtocol (), url.getUserInfo (), url.getHost (), url.getPort (), url.getPath (), url.getQuery (), url.getRef ()); urlStr = uri.toASCIIString (); urlStr.replace ( "http: //", "file: ///"); Non l'ho provato, ma penso che funzionerà .... :)
M Abdul Sami,

1
@tibi puoi semplicemente usare il metodo uri.toString () per convertirlo in stringa anziché in stringa Ascii.
M Abdul Sami,

1
L'API con cui stavo lavorando non ha accettato la +sostituzione degli spazi, ma ha accettato% 20, quindi questa soluzione ha funzionato meglio di BalusC, grazie!
Julian Honma,

35

Guava 15 ha ora aggiunto una serie di semplici escapers URL .


1
Questi soffrono delle stesse sciocche regole di fuga di URLEncoder.
ore 2

3
non sono sicuro che abbiano il problema. si differenziano per esempio "+" o "% 20" per fuggire "" (form param o path param) che URLEncodernon lo fa.
Emmanuel Touzery,

1
Questo ha funzionato per me, ho appena sostituito la chiamata a URLEncoder () per chiamare a UrlEscapers.urlFragmentEscaper () e ha funzionato, non è chiaro se dovrei usare UrlEscapers.urlPathSegmentEscaper ().
Paul Taylor,

2
In realtà non ha funzionato per me perché a differenza di URLEncoder non codifica '+' lo lascia da solo, il server decodifica '+' come spazio mentre se uso URLEncoder '+' vengono convertiti in% 2B e correttamente decodificati in +
Paul Taylor

2
Aggiornamento link: UrlEscapers
mgaert,

6

La libreria Apache Http Components offre un'opzione accurata per la creazione e la codifica di parametri di query:

Con HttpComponents 4.x utilizzare - URLEncodedUtils

Per HttpClient 3.x utilizzare - EncodingUtil


6

Ecco un metodo che puoi usare nel tuo codice per convertire una stringa url e una mappa di parametri in una stringa url codificata valida contenente i parametri della query.

String addQueryStringToUrlString(String url, final Map<Object, Object> parameters) throws UnsupportedEncodingException {
    if (parameters == null) {
        return url;
    }

    for (Map.Entry<Object, Object> parameter : parameters.entrySet()) {

        final String encodedKey = URLEncoder.encode(parameter.getKey().toString(), "UTF-8");
        final String encodedValue = URLEncoder.encode(parameter.getValue().toString(), "UTF-8");

        if (!url.contains("?")) {
            url += "?" + encodedKey + "=" + encodedValue;
        } else {
            url += "&" + encodedKey + "=" + encodedValue;
        }
    }

    return url;
}

6
URL url= new URL("http://example.com/query?q=random word £500 bank $");
URI uri = new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
String correctEncodedURL=uri.toASCIIString(); 
System.out.println(correctEncodedURL);

stampe

http://example.com/query?q=random%20word%20%C2%A3500%20bank%20$

Cosa sta succedendo qui?

1. Suddividere l'URL in parti strutturali. Usalo java.net.URL per questo.

2. Codificare correttamente ogni parte strutturale!

3. Utilizzare IDN.toASCII(putDomainNameHere)per Punycode codificare il nome host!

4. Usare java.net.URI.toASCIIString()per codificare in percentuale, Unicode con codifica NFC - (meglio sarebbe NFKC!). Per maggiori informazioni vedi: Come codificare correttamente questo URL

In alcuni casi è consigliabile verificare se l'URL è già codificato . Sostituisci anche gli spazi codificati '+' con spazi codificati '% 20'.

Ecco alcuni esempi che funzioneranno correttamente

{
      "in" : "http://نامه‌ای.com/",
     "out" : "http://xn--mgba3gch31f.com/"
},{
     "in" : "http://www.example.com/‥/foo",
     "out" : "http://www.example.com/%E2%80%A5/foo"
},{
     "in" : "http://search.barnesandnoble.com/booksearch/first book.pdf", 
     "out" : "http://search.barnesandnoble.com/booksearch/first%20book.pdf"
}, {
     "in" : "http://example.com/query?q=random word £500 bank $", 
     "out" : "http://example.com/query?q=random%20word%20%C2%A3500%20bank%20$"
}

La soluzione supera circa 100 testcase forniti da Web Plattform Test .


1

In Android vorrei usare questo codice:

Uri myUI = Uri.parse ("http://example.com/query").buildUpon().appendQueryParameter("q","random word A3500 bank 24").build();

Dov'è Uriaandroid.net.Uri


10
Questo non utilizza l'API Java standard. Quindi specifica la libreria utilizzata.
Rmuller,

1

Nel mio caso, dovevo solo passare l'intero URL e codificare solo il valore di ciascun parametro. Non ho trovato un codice comune per farlo (!!), quindi ho creato questo piccolo metodo per fare il lavoro:

public static String encodeUrl(String url) throws Exception {
    if (url == null || !url.contains("?")) {
        return url;
    }

    List<String> list = new ArrayList<>();
    String rootUrl = url.split("\\?")[0] + "?";
    String paramsUrl = url.replace(rootUrl, "");
    List<String> paramsUrlList = Arrays.asList(paramsUrl.split("&"));
    for (String param : paramsUrlList) {
        if (param.contains("=")) {
            String key = param.split("=")[0];
            String value = param.replace(key + "=", "");
            list.add(key + "=" +  URLEncoder.encode(value, "UTF-8"));
        }
        else {
            list.add(param);
        }
    }

    return rootUrl + StringUtils.join(list, "&");
}

public static String decodeUrl(String url) throws Exception {
    return URLDecoder.decode(url, "UTF-8");
}

Utilizza org.apache.commons.lang3.StringUtils


-2
  1. Utilizzare questo : URLEncoder.encode (query, StandardCharsets.UTF_8.displayName ()); o questo: URLEncoder.encode (query, "UTF-8");
  2. Puoi usare il codice seguente.

    String encodedUrl1 = UriUtils.encodeQuery(query, "UTF-8");//not change 
    String encodedUrl2 = URLEncoder.encode(query, "UTF-8");//changed
    String encodedUrl3 = URLEncoder.encode(query, StandardCharsets.UTF_8.displayName());//changed
    
    System.out.println("url1 " + encodedUrl1 + "\n" + "url2=" + encodedUrl2 + "\n" + "url3=" + encodedUrl3);

4
Non corretto. Devi codificare i nomi e i valori dei parametri separatamente. La codifica dell'intera stringa di query codificherà anche i separatori =e &, il che non è corretto.
Marchese di Lorne,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.