การเข้ารหัส URL Java ของพารามิเตอร์สตริงการสืบค้น


710

บอกว่าฉันมี URL

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

และฉันมีแบบสอบถามที่ป้อนโดยผู้ใช้เช่น:

คำสุ่ม£ 500 ธนาคาร $

ฉันต้องการให้ผลลัพธ์เป็น URL ที่เข้ารหัสอย่างถูกต้อง:

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

อะไรคือวิธีที่ดีที่สุดในการบรรลุเป้าหมายนี้? ฉันลองURLEncoderและสร้างวัตถุ URI / URL แต่ไม่มีสิ่งใดที่ออกมาถูกต้องนัก


25
คุณหมายถึงอะไรโดย "ไม่มีพวกเขาออกมาอย่างถูกต้อง"?
Mark Elliot

2
ฉันใช้ URI.create และแทนที่ช่องว่างด้วย + ในการสอบถาม ที่ไซต์ลูกค้ามันแปลง + กลับเป็นช่องว่างเมื่อฉันเลือกสตริงการสืบค้น ที่ได้ผลสำหรับฉัน
ND27


ทำไมคุณถึงคาดหวังว่า $ จะเข้ารหัสเป็นเปอร์เซ็นต์
jschnasse

คำตอบ:


1151

URLEncoderเป็นวิธีที่จะไป คุณจะต้องเก็บไว้ในใจที่จะเข้ารหัสเพียงการสอบถามบุคคลชื่อพารามิเตอร์สตริงและ / หรือมูลค่าไม่ URL ทั้งหมดเพื่อตรวจสอบว่าไม่สตริงแบบสอบถามคั่นพารามิเตอร์ตัวละครมิได้พารามิเตอร์ค่าชื่อตัวละครที่คั่น&=

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

โปรดทราบว่าช่องว่างในพารามิเตอร์การสืบค้นจะถูกแทนด้วย+ไม่ใช่%20ซึ่งถูกต้องตามกฎหมาย โดย%20ปกติจะใช้เพื่อแสดงช่องว่างใน URI ตัวเอง (ส่วนก่อนอักขระตัวคั่นสตริง URI- แบบสอบถาม?) ไม่ใช่ในสตริงแบบสอบถาม (ส่วนหลัง?)

โปรดทราบว่ามีสามencode()วิธี หนึ่งรายการที่ไม่มีCharsetอาร์กิวเมนต์ที่สองและอีกStringอาร์กิวเมนต์ที่สองซึ่งเป็นข้อยกเว้นที่เลือก อันที่ไม่มีการCharsetโต้แย้งถูกคัดค้าน ห้ามใช้และระบุCharsetอาร์กิวเมนต์เสมอ Javadocแม้อย่างชัดเจนแนะนำให้ใช้การเข้ารหัส UTF-8 ได้รับคำสั่งจากRFC3986และW3C

อักขระอื่น ๆ ทั้งหมดไม่ปลอดภัยและถูกแปลงเป็นไบต์ตั้งแต่หนึ่งไบต์ขึ้นไปโดยใช้รูปแบบการเข้ารหัสบางส่วน จากนั้นแต่ละไบต์จะถูกแทนด้วยสตริงอักขระ 3 ตัว "% xy" โดยที่ xy คือการแสดงเลขฐานสิบหกสองหลักของไบต์ แนะนำโครงการการเข้ารหัสเพื่อใช้เป็น UTF-8 อย่างไรก็ตามสำหรับเหตุผลด้านความเข้ากันได้หากไม่ได้ระบุการเข้ารหัสดังนั้นจะใช้การเข้ารหัสเริ่มต้นของแพลตฟอร์ม

ดูสิ่งนี้ด้วย:


สามารถมีพารามิเตอร์ได้ 2 ประเภทใน URL สตริงการสืบค้น (ตามด้วย?) และพารามิเตอร์พา ธ (โดยทั่วไปจะเป็นส่วนหนึ่งของ URL เอง) ดังนั้นสิ่งที่เกี่ยวกับพารามิเตอร์เส้นทาง URLEncoder สร้าง + สำหรับพื้นที่แม้กระทั่งพารามิเตอร์พา ธ ในความเป็นจริงมันก็ไม่ได้จัดการอะไรนอกเหนือจากสตริงแบบสอบถาม นอกจากนี้พฤติกรรมนี้ไม่ได้ซิงค์กับเซิร์ฟเวอร์โหนด js ดังนั้นสำหรับฉันคลาสนี้เป็นของเสียและไม่สามารถใช้นอกเหนือจากสถานการณ์ที่เฉพาะเจาะจง / พิเศษ
sharadendu sinha

2
@sharadendusinha: ตามเอกสารและคำตอบURLEncoderสำหรับพารามิเตอร์การสืบค้นที่เข้ารหัส URL จะเป็นไปตามapplication/x-www-form-urlencodedกฎ พารามิเตอร์ของเส้นทางไม่เหมาะกับหมวดหมู่นี้ คุณต้องใช้ตัวเข้ารหัส URI แทน
BalusC

ตามที่ฉันคาดการณ์ว่าจะเกิดขึ้น ... ผู้ใช้สับสนเพราะเห็นได้ชัดว่าปัญหาคือคนต้องเข้ารหัสมากกว่าค่าพารามิเตอร์ เป็นกรณีที่หายากมากที่คุณต้องเข้ารหัสค่าพารามิเตอร์เท่านั้น ทำไมฉันถึงให้คำตอบแบบ wiki "สับสน" เพื่อช่วยคนอย่าง @sharadendusinha
Adam Gent

1
@WijaySharma: เนื่องจากอักขระเฉพาะ URL จะได้รับการเข้ารหัสเช่นกัน คุณควรทำเช่นนั้นเมื่อคุณต้องการส่ง URL ทั้งหมดเป็นพารามิเตอร์การสืบค้นของ URL อื่น
BalusC

1
"+ ไม่ใช่% 20" คือสิ่งที่ฉันต้องการได้ยิน ขอบคุณมาก.
wetjosh

173

URLEncoderฉันจะไม่ใช้ นอกเหนือจากการตั้งชื่ออย่างไม่ถูกต้อง ( URLEncoderไม่มีส่วนเกี่ยวข้องกับ URL) ประสิทธิภาพ (ใช้ตัวสร้างStringBufferแทนและทำสิ่งอื่น ๆ สองสามอย่างที่ช้า) นอกจากนี้ยังง่ายเกินไปที่จะทำให้เสีย

แต่ผมจะใช้URIBuilderหรือฤดูใบไม้ผลิorg.springframework.web.util.UriUtils.encodeQueryHttpClientหรือคอมมอนส์อาปาเช่ เหตุผลที่คุณต้องหลีกเลี่ยงชื่อพารามิเตอร์การสืบค้น (เช่นคำตอบของ BalusC q) แตกต่างจากค่าพารามิเตอร์

เพียงข้อเสียในการข้างต้น (ที่ผมพบว่าการเจ็บปวด) คือURL ของไม่ได้เป็นเซตที่แท้จริงของ URI

รหัสตัวอย่าง:

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

เนื่องจากฉันเพิ่งลิงก์ไปยังคำตอบอื่น ๆ ฉันจึงทำเครื่องหมายว่านี่เป็นวิกิชุมชน รู้สึกอิสระที่จะแก้ไข


2
ทำไมมันไม่มีส่วนเกี่ยวข้องกับ URL?
ลูอิส Sep

15
@Luis: URLEncoderเป็น Javadoc ของมันกล่าวว่า intented พารามิเตอร์สตริงแบบสอบถามเข้ารหัสสอดคล้องapplication/x-www-form-urlencodedตามที่อธิบายไว้ใน HTML ข้อมูลจำเพาะ: w3.org/TR/html4/interact/... ผู้ใช้บางคนสร้างความสับสน / ใช้ในทางที่ผิดเพื่อเข้ารหัส URI ทั้งหมดเหมือนอย่างที่ผู้ตอบในปัจจุบันทำ
BalusC

8
@LuisSep โดยย่อ URLEncoder ใช้สำหรับการเข้ารหัสเพื่อส่งแบบฟอร์ม มันไม่ได้สำหรับการหลบหนี การหลบหนีไม่เหมือนกันอย่างแน่นอนที่คุณจะใช้เพื่อสร้าง URL ที่จะใส่ในหน้าเว็บของคุณ แต่ดูเหมือนจะคล้ายกันมากพอที่คนจะใช้ในทางที่ผิด ครั้งเดียวที่คุณควรใช้ URLEncoder คือถ้าคุณเขียนไคลเอนต์ HTTP (และยังมีตัวเลือกที่ยอดเยี่ยมสำหรับการเข้ารหัส)
Adam Gent

1
@BalusC " ผู้ใช้บางคนสร้างความสับสน / ใช้ในทางที่ผิดเพื่อเข้ารหัส URI ทั้งหมดเช่นเดียวกับที่ผู้ตอบในปัจจุบันเห็นได้ชัด " คุณคิดผิด ฉันไม่เคยพูดว่าฉันเมาไปกับมัน ฉันเพิ่งเห็นคนอื่นที่ทำไปแล้วใครที่เป็นโรคจิตต้องแก้ไข ส่วนที่ฉันเมาคือคลาส Java URL จะยอมรับวงเล็บเหลี่ยมที่ไม่ใช้ค่า Escape แต่ไม่ใช่คลาส URI มีวิธีมากมายในการไขการสร้าง URL และไม่ใช่ทุกคนที่ยอดเยี่ยมเหมือนคุณ ฉันจะบอกว่าผู้ใช้ส่วนใหญ่ที่กำลังมองหา SO สำหรับ URLEncoding อาจเป็น " ผู้ใช้สับสน / ละเมิด " URI หนี
Adam Gent

1
คำถามไม่ได้เกี่ยวกับเรื่องนั้น แต่คำตอบของคุณก็หมายความว่า
BalusC

99

คุณต้องสร้าง URI ก่อนเช่น:

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

จากนั้นแปลง Uri นั้นเป็นสตริง ASCII:

urlStr=uri.toASCIIString();

ตอนนี้สตริง url ของคุณได้รับการเข้ารหัสอย่างสมบูรณ์ก่อนอื่นเราทำการเข้ารหัส url อย่างง่ายจากนั้นเราแปลงเป็นสตริง ASCII เพื่อให้แน่ใจว่าไม่มีอักขระนอก US-ASCII เหลืออยู่ในสตริง นี่เป็นวิธีที่เบราว์เซอร์ทำ


7
ขอบคุณ! มันโง่ที่โซลูชันของคุณใช้งานได้ แต่ในตัวURL.toURI()ไม่ได้
user11153

2
น่าเสียดายที่นี่ดูเหมือนจะไม่ทำงานกับ "file: ///" (เช่น: "file: /// some / directory / ไฟล์ที่มีช่องว่าง. html"); มันระเบิดด้วย MalformedURLException ใน "new URL ()"; ความคิดวิธีการแก้ไขปัญหานี้?
ZioByte

คุณต้องทำสิ่งนี้: String urlStr = " some / directory / a file ที่มีช่องว่าง. html"; URL url = URL ใหม่ (urlStr); URI uri = URI ใหม่ (url.getProtocol (), url.getUserInfo (), url.getHost (), url.getPort (), url.getPath (), url.getQuery (), url.getQuery (), urlStr = uri.toASCIIString (); urlStr.replace ( "http: //", "file: ///"); ฉันไม่ได้ทดสอบ แต่ฉันคิดว่ามันจะทำงาน .... :)
M อับดุล Sami

1
@tibi คุณสามารถใช้ uri.toString () วิธีการในการแปลงเป็นสตริงแทนสตริง Ascii
M Abdul Sami

1
API ที่ฉันทำงานด้วยไม่ยอมรับการ+แทนที่ช่องว่าง แต่ยอมรับ% 20 ดังนั้นโซลูชันนี้ทำงานได้ดีกว่า BalusC ขอบคุณ!
Julian Honma

35

ฝรั่ง 15 ได้เพิ่มในขณะนี้ชุดของ escapers


1
สิ่งเหล่านี้ประสบกับกฎการหลบหนีที่โง่เขลาเหมือนURLEncoderกัน
2rs2ts

3
ไม่แน่ใจว่าพวกเขามีปัญหา พวกเขาแยกแยะความแตกต่างเช่น "+" หรือ "% 20" เพื่อหนี "" (รูปแบบพารามิเตอร์หรือเส้นทางพารามิเตอร์) ซึ่งURLEncoderไม่ได้
Emmanuel Touzery

1
สิ่งนี้ใช้ได้ผลสำหรับฉันฉันเพิ่งเปลี่ยนการเรียกไปยัง URLEncoder () เพื่อเรียกไปยัง UrlEscapers.urlFragmentEscaper () และใช้งานได้ไม่ชัดเจนถ้าฉันควรใช้ UrlEscapers.urlPathSegmentEscaper () แทน
พอลเทย์เลอร์

2
จริงๆแล้วมันไม่ได้ทำงานสำหรับฉันเพราะไม่เหมือน URLEncoder มันไม่ได้เข้ารหัส '+' ใบมันคนเดียวถอดรหัสเซิร์ฟเวอร์ '+' เป็นพื้นที่ในขณะที่ถ้าผมใช้ URLEncoder '+' s จะถูกแปลงเป็น% 2B และถูกต้องถอดรหัสกลับไป +
พอลเทย์เลอร์

2
อัปเดตลิงก์: UrlEscapers
mgaert

6

Apache Http Components เป็นตัวเลือกที่สมบูรณ์แบบสำหรับการสร้างและเข้ารหัสพารามิเตอร์การสืบค้น -

ด้วยการใช้ HttpComponents 4.x - URLEncodedUtils

สำหรับ HttpClient 3.x ให้ใช้ - EncodingUtil


6

นี่คือวิธีการที่คุณสามารถใช้ในรหัสของคุณเพื่อแปลงสตริง URL และแผนที่พารามิเตอร์เป็นสตริง URL ที่เข้ารหัสที่ถูกต้องซึ่งมีพารามิเตอร์แบบสอบถาม

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

พิมพ์

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

เกิดอะไรขึ้นที่นี่

1.แยก URL ออกเป็นส่วนโครงสร้าง ใช้java.net.URL สำหรับมัน

2. เข้ารหัสส่วนโครงสร้างอย่างถูกต้อง!

3.ใช้IDN.toASCII(putDomainNameHere)ในการPunycodeเข้ารหัสชื่อโฮสต์!

4.ใช้java.net.URI.toASCIIString()การเข้ารหัสเปอร์เซ็นต์เข้ารหัส Unicode Unicode - (ดีกว่าน่าจะเป็น NFKC!) สำหรับข้อมูลเพิ่มเติมดู: วิธีการเข้ารหัส URL นี้อย่างถูกต้อง

ในบางกรณีขอแนะนำให้ตรวจสอบว่ามีการเข้ารหัส URL แล้วหรือไม่ นอกจากนี้ให้แทนที่ช่องว่างที่เข้ารหัส '+' ด้วยช่องว่างที่เข้ารหัส '% 20'

นี่คือตัวอย่างบางส่วนที่สามารถทำงานได้อย่างถูกต้อง

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

วิธีการแก้ปัญหาผ่านไปประมาณ 100 ของ testcases ที่ได้รับจากการทดสอบเว็บ Plattform


1

ใน android ฉันจะใช้รหัสนี้:

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

ที่ไหนUriเป็นandroid.net.Uri


10
นี่ไม่ได้ใช้ Java API มาตรฐาน ดังนั้นโปรดระบุห้องสมุดที่ใช้
rmuller

1

ในกรณีของฉันฉันแค่ต้องผ่าน url ทั้งหมดและเข้ารหัสเฉพาะค่าของแต่ละพารามิเตอร์ ฉันไม่พบรหัสทั่วไปที่จะทำเช่นนั้น (!!) ดังนั้นฉันจึงสร้างวิธีการขนาดเล็กนี้เพื่อทำงาน:

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


-2
  1. ใช้สิ่งนี้: URLEncoder.encode (แบบสอบถาม, StandardCharsets.UTF_8.displayName ()); หรือสิ่งนี้: URLEncoder.encode (แบบสอบถาม, "UTF-8");
  2. คุณสามารถใช้รหัส follwing

    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
ไม่ถูกต้อง. คุณต้องเข้ารหัสชื่อพารามิเตอร์และค่าแยกกัน การเข้ารหัสสตริงข้อความค้นหาทั้งหมดจะเข้ารหัส=และ&ตัวคั่นซึ่งไม่ถูกต้อง
user207421
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.