Sorgu dizesi parametrelerinin Java URL kodlaması


711

Bir URL'm olduğunu söyle

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

ve kullanıcı tarafından girilen bir sorgu var gibi:

rastgele kelime £ 500 banka $

Sonucun doğru kodlanmış bir URL olmasını istiyorum:

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

Bunu başarmanın en iyi yolu nedir? URLEncoderURI / URL nesneleri oluşturmayı denedim ama hiçbiri doğru çıkmadı.


25
"Hiçbiri çok doğru çıkmıyor" ile ne demek istiyorsun?
Mark Elliot

2
Ben URI.create kullandım ve querystring boşluk + ile değiştirildi. Ben sorgu dizeleri seçtiğinizde istemci sitesinde + boşluklara geri dönüştürdü. Bu benim için çalıştı.
ND27


$ 'In neden yüzde olarak kodlanmasını bekliyorsunuz?
jschnasse

Yanıtlar:


1152

URLEncodergitmek için bir yoldur. Sorgu dizesi parametre ayırıcı karakterini veya parametre adı-değer ayırıcı karakterini değil, yalnızca URL'yi değil, yalnızca tek tek sorgu dizesi parametre adını ve / veya değerini kodlamayı aklınızda bulundurmanız gerekir .&=

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

Sorgu parametrelerindeki boşlukların , meşru olarak geçerli olan ile temsil +edilmediğini unutmayın %20. %20Genellikle URI kendisi (URI sorgu dizesi ayırıcı karakteri önceki kısım boşluk temsil etmek kullanılacak olan ?değil, sorgu dizesindeki) (kısmen sonra ?).

Ayrıca üç encode()yöntem olduğunu unutmayın . Biri Charsetikinci bağımsız değişken ve diğeri Stringikinci bağımsız değişken olarak kontrol edilen bir istisna atar. Olmadan bir Charsetargüman kullanımdan kaldırıldı. Asla kullanmayın ve her zaman Charsetargümanı belirtin . Javadoc emrince bile açıkça, UTF-8 kodlaması kullanmak önerir RFC3986 ve W3C .

Diğer tüm karakterler güvensizdir ve önce bazı kodlama şeması kullanılarak bir veya daha fazla bayta dönüştürülür. Daha sonra her bayt, 3 karakterlik "% xy" dizesi ile temsil edilir; burada xy, baytın iki basamaklı onaltılı gösterimidir. Kullanılması önerilen kodlama şeması UTF-8'dir . Bununla birlikte, uyumluluk nedeniyle, bir kodlama belirtilmezse, platformun varsayılan kodlaması kullanılır.

Ayrıca bakınız:


URL'de 2 tür parametre olabilir. Sorgu dizesi (ardından?) Ve yol parametresi (Genellikle URL'nin kendisinin bir parçası). Peki ya yol parametreleri. URLEncoder, yol parametreleri için bile boşluk için + üretir. Aslında sadece sorgu dizesi dışında bir şey işlemez. Ayrıca, bu davranış düğüm js sunucuları ile eşit değil. Yani benim için bu sınıf bir israftır ve çok özel / özel senaryolar dışında kullanılamaz.
sharadendu sinha

2
@sharadendusinha: belgelendiği ve yanıtlandığı gibi URLEncoderURL kodlu sorgu parametreleri application/x-www-form-urlencodedkurallarına uygundur . Yol parametreleri bu kategoriye uymuyor. Bunun yerine bir URI kodlayıcıya ihtiyacınız var.
BalusC

Tahmin edebileceğim gibi, kullanıcıların kafası karışıyor çünkü sorun, insanların sadece parametre değerinden daha fazlasını kodlaması gerektiğidir. Sadece bir parametre değerini kodlamanız gereken çok nadir bir durumdur. Bu yüzden @sharadendusinha gibi insanlara yardım etmek için "karışık" wiki yanıtımı sağladım.
Adam Gent

1
@WijaySharma: URL'ye özgü karakterler de kodlanacaktı. Bunu yalnızca URL'nin tamamını başka bir URL'nin sorgu parametresi olarak iletmek istediğinizde yapmanız gerekir.
BalusC

1
Duymak istediğim şey "+,% 20 değil". Çok teşekkür ederim.
wetjosh

173

Ben kullanmazdım URLEncoder. Yanlış adlandırılmasının yanı sıra ( URLEncoderURL'lerle hiçbir ilgisi yoktur), verimsiz ( StringBufferOluşturucu yerine kullanılır ve yavaş olan birkaç şey daha yapar) Vidalamak da çok kolay.

Bunun yerine URIBuilderveya Spring's org.springframework.web.util.UriUtils.encodeQueryveya Commons Apache kullanırdımHttpClient . Bunun nedeni, sorgu parametrelerinin adından (yani BalusC'nin yanıtı q) parametre değerinden farklı olarak kaçmak zorunda olmanızdır .

Yukarıdaki (sadece acıyla öğrendim) olumsuz sadece URL'leri URI gerçek bir alt kümesi değildir .

Basit kod:

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

Sadece diğer cevaplara bağladığım için bunu bir topluluk wiki'si olarak işaretledim. Düzenlemek için çekinmeyin.


2
URL'lerle neden hiçbir ilgisi yok?
Luis Eylül

15
@Luis: Javadoc'un HTML URLEncoderdize application/x-www-form-urlencoded: w3.org/TR/html4/interact/… ' de açıklandığı gibi sorgu dizesi parametrelerini kodlamayı amaçladığı gibi . Bazı kullanıcılar, mevcut yanıtlayıcının yaptığı gibi, tüm URI'ları kodlamak için gerçekten karıştırıyor / kötüye kullanıyor.
BalusC

8
@LuisSep kısaca URLEncoder form gönderimi için kodlama içindir. Kaçmak için değil. Bu , web sayfanıza konacak URL'ler oluşturmak için kullanacağınız tam olarak aynı değil, ancak insanların onu kötüye kullanmasına yeterince benziyor. URLEncoder'ı kullanmanız gereken tek zaman, bir HTTP istemcisi yazmanızdır (ve hatta o zaman kodlama için çok daha üstün seçenekler varsa).
Adam Gent

1
@BalusC " Bazı kullanıcılar gerçekten de mevcut yanıtlayıcının yaptığı gibi tüm URI'ları kodlamak için karıştırıyor / kötüye kullanıyor. " Yanlış kabul ettin. Bunu berbat ettiğimi hiç söylemedim. Bunu yapan başkalarını gördüm, düzeltmem gereken hatalar. Sıkıştığım kısım, Java URL sınıfının, çıkışsız köşeli parantezleri kabul etmesi, ancak URI sınıfını kabul etmemesidir. URL oluşturmanın pek çok yolu vardır ve herkes sizin gibi parlak değildir. Ben SO için URLEncoding arıyor çoğu kullanıcı muhtemelen " kullanıcılar gerçekten karıştırmayın / kötüye " URI kaçan olduğunu söyleyebilirim .
Adam Gent

1
Soru bununla ilgili değildi ama cevabınız bunu ima ediyor.
BalusC

99

Önce aşağıdaki gibi bir URI oluşturmanız gerekir:

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

Sonra bu Uri'yi ASCII dizesine dönüştürün:

urlStr=uri.toASCIIString();

Şimdi url dizeniz tamamen kodlanmış, önce basit url kodlaması yaptık ve sonra US-ASCII dışında hiçbir karakterin dizede kalmadığından emin olmak için ASCII String'e dönüştürdük. Tarayıcılar tam da böyle yapar.


7
Teşekkürler! Çözümünüzün çalışması aptalca, ama yerleşik URL.toURI()değil.
user11153

2
Ne yazık ki bu "file: ///" ile çalışmıyor (örn: "file: /// some / directory / spaces.html içeren bir dosya"); "yeni URL ()" de MalformedURLException ile bombalanır; bunu nasıl düzeltebilirim?
ZioByte

Bunun gibi bir şey yapmanız gerekir: String urlStr = " some / directory / spaces.html içeren bir dosya"; URL url = yeni URL (urlStr); URI uri = yeni URI (url.getProtocol (), url.getUserInfo (), url.getHost (), url.getPort (), url.getPath (), url.getQuery (), url.getRef ()); urlStr = uri.toASCIIString (); urlStr.replace ( "http: //", "file: ///"); Test etmedim, ama işe yarayacağını düşünüyorum .... :)
M Abdul Sami

1
@tibi sadece ascii dizesi yerine dizgiye dönüştürmek için uri.toString () yöntemini kullanabilirsiniz.
M Abdul Sami

1
Birlikte çalıştığım API +, boşlukların yerini almayı kabul etmedi, ancak% 20'yi kabul etti, bu yüzden bu çözüm BalusC'den daha iyi çalıştı, teşekkürler!
Julian Honma

35

1
Bunlar aynı aptal kaçan kurallardan muzdariptir URLEncoder.
2rs2ts

3
sorun olduğundan emin değilim. örneğin "+" ya da "% 20" 'den kaçmak için farklılaşırlar "(form param ya da path param) URLEncoder.
Emmanuel Touzery

1
Bu benim için çalıştı sadece yerine UrlEscapers.urlFragmentEscaper () aramak için URLEncoder () çağrısını değiştirdi ve bunun yerine, UrlEscapers.urlPathSegmentEscaper () kullanmanız gerektiği açık değil çalıştı.
Paul Taylor

2
Aslında URLEncoder aksine o '+' o, tek başına sunucu kod okumalar '+' o kodlamak bırakır doesnt çünkü benim için hiç bir çalışma olarak ben URLEncoder kullanırsanız '+' ın + geri% 2B dönüştürülür ve doğru bir şekilde deşifre oysa uzay
Paul Taylor

2
Bağlantı güncellemesi: UrlEscapers
mgaert

6

Apache Http Components kütüphanesi, sorgu parametrelerini oluşturmak ve kodlamak için temiz bir seçenek sunar -

HttpComponents ile 4.x kullanımı - URLEncodedUtils

HttpClient 3.x kullanımı için - EncodingUtil


6

Kodunuzda bir URL dizesini ve parametre haritasını sorgu parametrelerini içeren geçerli bir kodlanmış URL dizesine dönüştürmek için kullanabileceğiniz bir yöntem.

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

Baskılar

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

Burada ne oluyor?

1. URL'yi yapısal parçalara ayırın. Bunun için kullanın java.net.URL .

2. Her yapısal parçayı uygun şekilde kodlayın!

3. Ana makine adını kodlamak IDN.toASCII(putDomainNameHere)için Punycode kullanın !

4.java.net.URI.toASCIIString() Yüzde kodlamak için kullanın , NFC kodlu unicode - (daha iyi NFKC olurdu!). Daha fazla bilgi için bkz. Bu URL nasıl doğru şekilde kodlanır?

Bazı durumlarda, URL'nin zaten kodlanmış olup olmadığını kontrol etmeniz önerilir . Ayrıca '+' kodlu boşlukları '% 20' kodlu boşluklarla değiştirin.

Ayrıca düzgün çalışacak bazı örnekler

{
      "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$"
}

Çözüm, Web Plattform Tests tarafından sağlanan yaklaşık 100 test senaryosundan geçmektedir .


1

Android'de bu kodu kullanırsınız:

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

Nerede Uribirandroid.net.Uri


10
Bu standart Java API'sini kullanmaz. Lütfen kullanılan kitaplığı belirtin.
rmuller

1

Benim durumumda sadece tüm url geçmek ve sadece her parametrenin değerini kodlamak gerekiyordu. Bunu yapmak için ortak bir kod bulamadık (!!) bu yüzden iş yapmak için bu küçük yöntemi yarattı:

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");
}

Org.apache.commons.lang3.StringUtils kullanır


-2
  1. Bu : URLEncoder.encode (sorgu, StandardCharsets.UTF_8.displayName ()); ya da bu: URLEncoder.encode (sorgu, "UTF-8");
  2. Aşağıdaki kodları kullanabilirsiniz.

    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
Doğru değil. Parametre adlarını ve değerlerini ayrı ayrı kodlamanız gerekir. Sorgu dizesinin tamamını kodlamak, =ve &doğru olmayan ayırıcıları da kodlar .
Lorne Marquis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.