Encoding Alamat URL HTTP di Jawa

366

Aplikasi Java mandiri saya mendapat URL (yang menunjuk ke suatu file) dari pengguna dan saya perlu menekan dan mengunduhnya. Masalah yang saya hadapi adalah saya tidak dapat menyandikan alamat URL HTTP dengan benar ...

Contoh:

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

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

mengembalikan saya:

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

Tapi, yang saya inginkan adalah

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

(ruang diganti oleh% 20)

saya tebak URLEncoder tidak dirancang untuk menyandikan URL HTTP ... JavaDoc mengatakan "Kelas utilitas untuk penyandian formulir HTML" ... Apakah ada cara lain untuk melakukan ini?

suDocker
sumber
Perilaku itu sepenuhnya benar. Penyandian URL adalah mengubah sesuatu menjadi string yang dapat dengan aman diteruskan sebagai parameter URL, dan sama sekali tidak diartikan sebagai URL. Padahal Anda ingin itu hanya mengkonversi satu bagian kecil dari URL.
Stephen Holt

Jawaban:

303

Kelas java.net.URI dapat membantu; dalam dokumentasi URL yang Anda temukan

Catatan, kelas URI melakukan pelarian bidang komponennya dalam keadaan tertentu. Cara yang disarankan untuk mengelola encoding dan decoding URL adalah dengan menggunakan URI

Gunakan salah satu konstruktor dengan lebih dari satu argumen, seperti:

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

(konstruktor argumen tunggal URI TIDAK lepas dari karakter ilegal)


Hanya karakter ilegal yang lolos dengan kode di atas - ia TIDAK lepas dari karakter non-ASCII (lihat komentar fatih).
The toASCIIStringmetode dapat digunakan untuk mendapatkan String hanya dengan karakter US-ASCII:

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

Untuk URL dengan kueri suka http://www.google.com/ig/api?weather=São Paulo, gunakan versi 5-parameter dari konstruktor:

URI uri = new URI(
        "http", 
        "www.google.com", 
        "/ig/api",
        "weather=São Paulo",
        null);
String request = uri.toASCIIString();
pengguna85421
sumber
13
Harap dicatat, kelas URI yang disebutkan di sini adalah dari "org.apache.commons.httpclient.URI" bukan "java.net", "java.net" tidak URI tidak menerima karakter ilegal, kecuali jika Anda akan menggunakan konstruktor yang membangun URL dari komponennya, seperti cara yang disebutkan dalam komentar Matt di bawah ini
Mohamed Faramawi
7
@ Mohamed: kelas yang saya sebutkan dan digunakan untuk pengujian sebenarnya adalah java.net.URI : ia bekerja dengan sempurna (Java 1.6). Saya akan menyebutkan nama kelas yang sepenuhnya memenuhi syarat jika itu bukan Java standar dan tautan menunjuk ke dokumentasi java.net.URI. Dan, dengan komentar Sudhakar, itu memecahkan masalah tanpa menyertakan "perpustakaan umum"!
user85421
1
URI uri = URI baru ("http", "search.barnesandnoble.com", "/ booksearch / é", null); Tidakkah melarikan diri dengan sampel ini benar? Ini seharusnya diloloskan dengan% escapes
fmucar
@ fatih - itu benar, terima kasih! Biasanya itu seharusnya tidak menjadi masalah, tetapi ada solusi sederhana - hampir sama seperti yang saya tulis sebelumnya. Lihat hasil edit ke-2.
user85421
@Carlos Thx untuk edit. Sekarang memang melarikan diri tetapi tidak benar melarikan diri. Itu harus menambahkan% ke nilai HEX dari char untuk Path params yang berarti é char harus dikonversi ke% e9
fmucar
91

Harap diingat bahwa sebagian besar jawaban di atas tidak benar.

The URLEncoderkelas, meskipun nama, adalah bukan apa yang perlu di sini. Sangat disayangkan Sun menamakan kelas ini dengan sangat menyebalkan. URLEncoderdimaksudkan untuk meneruskan data sebagai parameter, bukan untuk menyandi URL itu sendiri.

Dengan kata lain, "http://search.barnesandnoble.com/booksearch/first book.pdf"adalah URL. Parameter akan, misalnya "http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that",. Parameternya adalah apa yang akan Anda gunakan URLEncoder.

Dua contoh berikut menyoroti perbedaan di antara keduanya.

Berikut ini menghasilkan parameter yang salah, sesuai dengan standar HTTP. Perhatikan bahwa ampersand (&) dan plus (+) dikodekan secara salah.

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

Berikut ini akan menghasilkan parameter yang benar, dengan kueri disandikan dengan benar. Perhatikan spasi, tanda bintang, dan tanda 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
Mat
sumber
2
Itu benar, konstruktor URI sudah mengkodekan querystring, menurut dokumentasi 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
@Raemon Jawabannya benar tetapi menggunakan string kueri dengan cara yang tidak biasa; contoh yang lebih normal mungkin query = URLEncoder.encode(key) + "=" + URLEncoder.encode(value). Dokumen hanya mengatakan bahwa "karakter apa pun yang bukan karakter URI legal dikutip".
tc.
1
Saya setuju dengan Matt di sini. Jika Anda mengetik URL ini: " google.com/help/me/book name + me /? MY CRZY QUERY! + & + :)" di browser, URL akan secara otomatis menyandikan spasi tetapi "&" digunakan sebagai nilai permintaan pemisah dan "+" hilang.
arcot
80

Saya akan menambahkan satu saran di sini yang ditujukan untuk pengguna Android. Anda dapat melakukan ini yang menghindari keharusan mendapatkan perpustakaan eksternal. Selain itu, semua solusi pencarian / penggantian karakter yang disarankan dalam beberapa jawaban di atas berbahaya dan harus dihindari.

Cobalah ini:

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

Anda dapat melihat bahwa di URL khusus ini, saya harus memiliki ruang-ruang yang disandikan agar saya dapat menggunakannya untuk permintaan.

Ini memanfaatkan beberapa fitur yang tersedia untuk Anda di kelas Android. Pertama, kelas URL dapat memecah url menjadi komponen yang tepat sehingga Anda tidak perlu melakukan pencarian string / mengganti pekerjaan. Kedua, pendekatan ini mengambil keuntungan dari fitur kelas URI dari komponen yang lolos dengan benar ketika Anda membangun URI melalui komponen daripada dari string tunggal.

Keindahan dari pendekatan ini adalah bahwa Anda dapat mengambil string url yang valid dan membuatnya bekerja tanpa memerlukan pengetahuan khusus tentang itu sendiri.

Craig B
sumber
3
Pendekatan yang bagus, tetapi saya ingin menunjukkan bahwa kode ini tidak mencegah pengkodean ganda , misalnya% 20 dikodekan ke% 2520. Jawaban Scott tidak menderita dari ini.
nattster
2
Itu tidak bisa menangani #.
Alston
Atau jika Anda hanya ingin melakukan kutipan path: URI baru (null, null, "/ path with spasi", null, null) .toString ()
user1050755
1
@Stallman Jika nama file Anda berisi #, kelas URL akan memasukkannya ke "ref" (setara dengan "fragmen" di kelas URI). Anda dapat mendeteksi apakah URL.getRef () mengembalikan sesuatu yang mungkin diperlakukan sebagai bagian dari jalur dan meneruskan URL.getPath () + "#" + URL.getRef () sebagai parameter "path" dan null sebagai fragmen " "parameter konstruktor parameter kelas 7 URI. Secara default, string setelah # diperlakukan sebagai referensi (atau jangkar).
gouessej
49

solusi yang saya kembangkan dan jauh lebih stabil daripada yang lain:

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
sumber
3
itu juga mengharuskan Anda untuk memecah url menjadi beberapa bagian. Tidak ada cara bagi komputer untuk mengetahui bagian mana dari url yang disandikan. Lihat hasil edit saya di atas
fmucar
4
@ fmucar Terima kasih atas kode itu! Perlu dicatat bahwa ini bukan UTF-8. Untuk mendapatkan UTF-8 cukup pra-proses input dengan String utf8Input = new String(Charset.forName("UTF-8").encode(input).array());(diambil dari sini )
letmaik
1
Solusi ini sebenarnya juga akan menyandikan "http: //" bagian menjadi "http% 3A% 2F% 2F", yang merupakan pertanyaan awal yang coba dihindari.
Benjamin Piette
2
Anda hanya memberikan apa yang Anda perlu untuk menyandikan, bukan seluruh URL. Tidak ada cara untuk melewatkan satu string URL keseluruhan dan mengharapkan penyandian yang benar. Dalam semua kasus, Anda perlu memecah url menjadi potongan-potongan logisnya.
fmucar
2
Saya punya masalah dengan jawaban ini karena tidak menyandikan karakter yang tidak aman ke UTF-8 .. mungkin tergantung pada aplikasi rekan.
Tarnschaf
36

Jika Anda memiliki URL, Anda dapat mengirimkan url.toString () ke metode ini. Dekode pertama, untuk menghindari penyandian ganda (misalnya, penyandian ruang menghasilkan% 20 dan penyandian tanda persen menghasilkan% 25, ​​jadi penyandian ganda akan mengubah ruang menjadi% 2520). Kemudian, gunakan URI seperti dijelaskan di atas, menambahkan semua bagian URL (sehingga Anda tidak menjatuhkan parameter kueri).

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
sumber
1
URLDecoder.decode (string, "UTF-8") gagal dengan IllegalArgumentException ketika Anda meneruskan string sebagai " google.co.in/search?q=123%!123 ". Ini adalah URL yang valid. Saya kira API ini tidak berfungsi ketika% digunakan sebagai data alih-alih karakter penyandian.
MediumOne
26

Ya penyandian URL akan menyandikan string itu sehingga akan diteruskan dengan benar di url ke tujuan akhir. Misalnya Anda tidak dapat memiliki http://stackoverflow.com?url=http://yyy.com . UrlEncoding parameter akan memperbaiki nilai parameter itu.

Jadi saya punya dua pilihan untuk Anda:

  1. Apakah Anda memiliki akses ke jalur yang terpisah dari domain? Jika demikian, Anda mungkin dapat dengan mudah UrlEncode the path. Namun, jika ini bukan masalahnya maka opsi 2 mungkin cocok untuk Anda.

  2. Dapatkan commons-httpclient-3.1. Ini memiliki kelas URIUtil:

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

Ini akan menampilkan apa yang Anda cari, karena hanya akan menyandikan bagian jalur URI.

FYI, Anda akan memerlukan commons-codec dan commons-logging untuk metode ini agar berfungsi saat runtime.

Nathan Feger
sumber
Sidenote apache commons berhenti mempertahankan URIUtil di cabang 4.x, menyarankan Anda menggunakan kelas URI JDK sebagai gantinya. Berarti Anda harus memutuskan tali sendiri.
Nicholi
2) Persis juga disarankan di sini stackoverflow.com/questions/5330104/... Saya juga menggunakan URIUtilsolusi
To Kra
11

Nitpicking: string yang berisi karakter spasi putih menurut definisi bukan URI. Jadi yang Anda cari adalah kode yang mengimplementasikan pelolosan URI yang didefinisikan dalam Bagian 2.1 dari RFC 3986 .

Julian Reschke
sumber
Kita membutuhkan "bagaimana" dalam jawaban, bukan "apa".
shinzou
11

Sayangnya, org.apache.commons.httpclient.util.URIUtilsudah usang, dan replacement org.apache.commons.codec.net.URLCodecapakah pengkodean cocok untuk posting bentuk, bukan di URL yang sebenarnya. Jadi saya harus menulis fungsi saya sendiri, yang melakukan komponen tunggal (tidak cocok untuk seluruh string kueri yang dimiliki? 'Dan &' 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
sumber
Ayolah, harus ada perpustakaan yang melakukan ini.
shinzou
9

URLEncoding dapat menyandikan URL HTTP dengan baik, seperti yang sayangnya Anda temukan. String yang Anda masukkan, " http://search.barnesandnoble.com/booksearch/first book.pdf", telah dikodekan dengan benar dan sepenuhnya ke dalam bentuk yang disandikan URL. Anda bisa meneruskan seluruh rangkaian panjang gobbledigook yang Anda dapatkan kembali sebagai parameter dalam URL, dan itu bisa didekodekan kembali menjadi string yang Anda lewati.

Sepertinya Anda ingin melakukan sesuatu yang sedikit berbeda daripada meneruskan seluruh URL sebagai parameter. Dari apa yang saya kumpulkan, Anda mencoba membuat URL pencarian yang terlihat seperti " http://search.barnesandnoble.com/booksearch/wh whateverTheUserPassesIn ". Satu-satunya hal yang perlu Anda enkode adalah bit "whateverTheUserPassesIn", jadi mungkin yang perlu Anda lakukan adalah sesuatu seperti ini:

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

Itu akan menghasilkan sesuatu yang lebih valid untuk Anda.

Brandon Yarbrough
sumber
17
Itu akan menggantikan spasi di userInput dengan "+". Poster membutuhkannya diganti dengan "% 20".
vocaro
@vocaro: itu poin yang sangat bagus. URLEncoder lolos seperti argumen adalah parameter kueri, tidak seperti URL lainnya.
Brandon Yarbrough
9

Jika ada yang tidak ingin menambahkan ketergantungan pada proyek mereka, fungsi-fungsi ini mungkin bermanfaat.

Kami melewati bagian 'jalur' dari URL kami ke sini. Anda mungkin tidak ingin meneruskan URL lengkap sebagai parameter (string kueri perlu escapes yang berbeda, dll).

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

Dan tes:

@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
sumber
Terima kasih untuk ini, tetapi apa yang harus saya lakukan untuk menyandikan spasi -> gunakan% 20 sebagai gantinya sebagai contoh Anda?
N00b Pr0grammer
Diperbarui untuk memperhitungkan ruang sebagai% 20
Cuga
7

Masih ada masalah jika Anda memiliki kode "/" (% 2F) yang disandikan di URL Anda.

RFC 3986 - Bagian 2.2 mengatakan: "Jika data untuk komponen URI akan bertentangan dengan tujuan karakter yang dicadangkan sebagai pembatas, maka data yang bertentangan harus dikodekan persen sebelum URI dibentuk." (RFC 3986 - Bagian 2.2)

Tapi ada Masalah dengan Tomcat:

http://tomcat.apache.org/security-6.html - Diperbaiki di Apache Tomcat 6.0.10

penting: Direktori traversal CVE-2007-0450

Tomcat mengizinkan '\', '% 2F' dan '% 5C' [...].

Properti sistem Java berikut telah ditambahkan ke Tomcat untuk memberikan kontrol tambahan dalam penanganan pemisah jalur di URL (kedua opsi default ke false):

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

Karena ketidakmungkinan untuk menjamin bahwa semua URL ditangani oleh Tomcat karena mereka berada di server proxy, Tomcat harus selalu diamankan seolah-olah tidak ada akses konteks pembatasan proxy yang digunakan.

Mempengaruhi: 6.0.0-6.0.9

Jadi, jika Anda memiliki URL dengan karakter% 2F, Tomcat mengembalikan: "400 URI tidak valid: noSlash"

Anda dapat beralih dari perbaikan bug di skrip startup Tomcat:

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

Saya membaca jawaban sebelumnya untuk menulis metode saya sendiri karena saya tidak dapat melakukan sesuatu dengan benar menggunakan solusi dari jawaban sebelumnya, itu terlihat bagus untuk saya tetapi jika Anda dapat menemukan URL yang tidak berfungsi dengan ini, beri tahu saya.

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
sumber
4

Saya setuju dengan Matt. Memang, saya belum pernah melihatnya dengan baik dijelaskan dalam tutorial, tetapi satu hal adalah bagaimana menyandikan jalur URL, dan yang sangat berbeda adalah bagaimana menyandikan parameter yang ditambahkan ke URL (bagian permintaan, di belakang "? " simbol). Mereka menggunakan pengkodean yang serupa, tetapi tidak sama.

Khusus untuk pengkodean karakter spasi putih. Jalur URL harus dikodekan sebagai% 20, sedangkan bagian kueri memungkinkan% 20 dan juga tanda "+". Ide terbaik adalah mengujinya sendiri terhadap server Web kami, menggunakan browser Web.

Untuk kedua kasus, saya SELALU akan menyandikan KOMPONEN DENGAN KOMPONEN , tidak pernah seluruh string. Memang URLEncoder memungkinkan itu untuk bagian permintaan. Untuk bagian jalur Anda bisa menggunakan kelas URI, meskipun dalam kasus ini meminta seluruh string, bukan komponen tunggal.

Bagaimanapun, saya percaya bahwa cara terbaik untuk menghindari masalah ini adalah dengan menggunakan desain pribadi yang tidak konflik. Bagaimana? Sebagai contoh, saya tidak akan pernah memberi nama direktori atau parameter menggunakan karakter lain selain aZ, AZ, 0-9 dan _. Dengan begitu, satu-satunya kebutuhan adalah menyandikan nilai setiap parameter, karena mungkin berasal dari input pengguna dan karakter yang digunakan tidak diketahui.

negora
sumber
2
kode sampel menggunakan URL dalam pertanyaan akan menjadi hal yang baik untuk dimasukkan ke dalam jawaban Anda
Martin Serrano
3

Mungkin dapat mencoba UriUtils di org.springframework.web.util

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

Anda juga dapat menggunakan GUAVAdan mengarahkan escaper: UrlEscapers.urlFragmentEscaper().escape(relativePath)

Untuk Kra
sumber
2

Selain balasan Carlos Heuberger: jika diperlukan perbedaan dari standar (80), konstruktor 7 param harus digunakan:

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
sumber
2

Saya mengambil konten di atas dan mengubahnya sedikit. Saya suka logika positif terlebih dahulu, dan saya pikir HashSet mungkin memberikan kinerja yang lebih baik daripada beberapa opsi lain, seperti mencari melalui String. Meskipun, saya tidak yakin apakah penalti autoboxing sepadan, tetapi jika kompiler mengoptimalkan untuk karakter ASCII, maka biaya tinju akan rendah.

/***
 * 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
sumber
1

Gunakan solusi Java standar berikut (melewati 100 testcases yang disediakan oleh Web Plattform Tests ):

0. Uji apakah URL sudah dikodekan .

1. Pisahkan URL menjadi bagian-bagian struktural. Gunakan java.net.URL untuk itu.

2. Encode setiap bagian struktural dengan benar!

3. Gunakan IDN.toASCII(putDomainNameHere)untuk Punycode menyandikan nama host!

4. Gunakan java.net.URI.toASCIIString()untuk persen-encode, NFC dikodekan unicode - (lebih baik NFKC!).

Temukan lebih lanjut di sini: https://stackoverflow.com/a/49796882/1485527

Jschnasse
sumber
0

Saya telah membuat proyek baru untuk membantu membangun URL HTTP. Perpustakaan akan secara otomatis menyandi URL segmen jalan dan parameter kueri.

Anda dapat melihat sumber dan mengunduh biner di https://github.com/Widen/urlbuilder

Contoh URL dalam pertanyaan ini:

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

menghasilkan

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

Uriah Carpenter
sumber
0

Saya memiliki masalah yang sama. Selesaikan ini dengan tidak:

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

Ini mengkodekan string tetapi melompat ":" dan "/".

Richard R
sumber
0

saya menggunakan ini

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

tambahkan ketergantungan ini

 <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>1.8</version>
    </dependency>
pengembang pelajari999
sumber
-2

Saya mengembangkan perpustakaan yang melayani tujuan ini: galimatias . Ini mem-parsing URL dengan cara yang sama seperti browser web. Artinya, jika URL berfungsi di browser, URL akan diurai dengan benar oleh galimatias .

Pada kasus ini:

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

Akan memberikan: http://search.barnesandnoble.com/booksearch/first%20book.pdf. Tentu saja ini adalah kasus yang paling sederhana, tetapi ini akan berhasil dengan apa pun, jauh dari itu java.net.URI.

Anda dapat memeriksanya di: https://github.com/smola/galimatias

smola
sumber
-3

Anda dapat menggunakan fungsi seperti ini. Lengkapi dan modifikasi sesuai kebutuhan Anda:

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

Contoh penggunaan:

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

Hasilnya adalah: http://www.growup.com/folder/int%C3%A9rieur-%C3%A0_vendre?o=4

Salim Hamidi
sumber
1
Jawaban ini tidak lengkap tanpa URLCodec.
Marquis of Lorne
upvote untuk chaining .replace (), itu tidak ideal tetapi cukup untuk kasus penggunaan ad-hoc dasar
svarog
-5

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

Ini akan menjadi konstan saya kira dan hanya perubahan nama file secara dyamically jadi dapatkan nama file

Nama file string; // dapatkan nama file

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

raja
sumber
2
Bagaimana dengan semua karakter ilegal lainnya?
Marquis of Lorne
-7

Bagaimana tentang:

UrlEncode String publik (String in_) {

String retVal = "";

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

return retVal;

}

MichaelICE
sumber
URLEncoder tidak dapat digunakan untuk menghindari karakter URL ivalid. Hanya untuk menyandikan formulir.
Archer