쿼리 문자열 매개 변수의 Java URL 인코딩


710

URL이 있다고 가정 해보십시오.

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

사용자가 입력 한 쿼리가 있습니다.

임의의 단어 £ 500 bank $

결과가 올바르게 인코딩 된 URL이되기를 원합니다.

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

이것을 달성하는 가장 좋은 방법은 무엇입니까? URLEncoderURI / URL 객체를 만들 려고 시도했지만 그중 어느 것도 제대로 나오지 않았습니다.


25
"아무도 옳지 않다"는 것은 무엇을 의미합니까?
Mark Elliot

2
URI를 사용하고 querystring에서 공백을 +로 바꿨습니다. 클라이언트 사이트에서 쿼리 문자열을 선택할 때 +를 공백으로 다시 변환했습니다. 그것은 나를 위해 일했다.
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 인코딩을 사용하는 것이 권고 RFC3986W3C .

다른 모든 문자는 안전하지 않으며 일부 인코딩 체계를 사용하여 먼저 하나 이상의 바이트로 변환됩니다. 그런 다음 각 바이트는 3 자 문자열 "% xy"로 표시됩니다. 여기서 xy는 바이트의 두 자리 16 진수 표현입니다. 권장되는 인코딩 체계는 UTF-8 입니다. 그러나 호환성을 위해 인코딩을 지정하지 않으면 플랫폼의 기본 인코딩이 사용됩니다.

또한보십시오:


URL에는 두 가지 유형의 매개 변수가있을 수 있습니다. 쿼리 문자열 (? 뒤에)과 경로 매개 변수 (일반적으로 URL 자체의 일부) 경로 매개 변수는 어떻습니까? URLEncoder는 경로 매개 변수에 대해서도 공간을 위해 +를 생성합니다. 실제로 쿼리 문자열 이외의 것은 처리하지 않습니다. 또한이 동작은 노드 js 서버와 동기화되지 않습니다. 나 에게이 클래스는 낭비이며 매우 구체적이고 특별한 시나리오 이외의 용도로는 사용할 수 없습니다.
sharadendu sinha

2
@sharadendusinha : 문서화되고 답변 된대로 URLEncoderURL 인코딩 된 쿼리 매개 변수는 application/x-www-form-urlencoded규칙을 준수 합니다. 경로 매개 변수가이 범주에 맞지 않습니다. 대신 URI 인코더가 필요합니다.
BalusC

내가 예상 한대로 ... 문제는 사람들이 단순히 매개 변수 값 이상을 인코딩해야하기 때문에 혼란 스럽습니다. 매개 변수 값만 인코딩하면되는 경우는 매우 드 case니다. 그래서 @sharadendusinha와 같은 사람들을 돕기 위해 "혼란 된"위키 답변을 제공했습니다.
Adam Gent

1
@WijaySharma : URL 특정 문자도 인코딩되기 때문입니다. 전체 URL을 다른 URL의 쿼리 매개 변수로 전달하려는 경우에만 수행해야합니다.
BalusC

1
"+, % 20이 아니라"는 내가 듣기 위해 필요한 것입니다. 정말 고맙습니다.
wetjosh 2009 년

173

나는 사용하지 않을 것이다 URLEncoder. 틀린 이름 외에 (URLEncoder URL과는 관련이 없음) 비효율적입니다 ( StringBuffer빌더 대신 사용하고 속도가 느린 몇 가지 다른 작업을 수행함) 또한 너무 쉽게 망칠 수 있습니다.

대신 URIBuilder또는 Spring org.springframework.web.util.UriUtils.encodeQuery또는 Commons Apache를 사용합니다.HttpClient 합니다. 쿼리 매개 변수 이름 (예 : BalusC 's answer q)을 매개 변수 값과 다르게 이스케이프해야하는 이유 입니다.

위의 유일한 단점은 (내가 고통스럽게 알게 된) URL URL의 진정한 하위 집합이 아니라는 것입니다 .

샘플 코드 :

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과 관련이없는 이유는 무엇입니까?
Luis Sep

15
@Luis : URLEncoder는 javadoc application/x-www-form-urlencoded이 HTML 스펙 w3.org/TR/html4/interact/…에 설명 된대로 쿼리 문자열 매개 변수를 인코딩 하려는 의도를 가지고 있습니다. 일부 사용자는 현재 응답자가 분명히 한 것처럼 전체 URI를 인코딩하는 데 혼동하거나 남용합니다.
BalusC

8
URLEncoder의 @LuisSep는 양식 제출을위한 인코딩입니다. 탈출하지 않습니다. 웹 페이지에 넣을 URL을 만드는 데 사용되는 것과 정확히 동일하게 이스케이프 되지는 않지만 사람들이 그것을 악용 할 정도로 유사합니다. URLEncoder를 사용해야하는 유일한 시간은 HTTP 클라이언트를 작성하는 경우뿐 아니라 인코딩에 대한 훨씬 뛰어난 옵션이있는 경우입니다.
Adam Gent

1
@BalusC " 일부 사용자는 현재 응답자가 분명히 한 것처럼 전체 URI를 인코딩하는 데 혼동 을가 하거나 남용합니다. ". 당신은 잘못 생각했습니다. 나는 그것을 망쳤다 고 말한 적이 없다. 방금 다른 사람을 보았습니다. 누가 내가 고쳐야 할 버그입니까? 내가 망친 부분은 Java URL 클래스가 이스케이프 처리되지 않은 대괄호를 허용하지만 URI 클래스는 허용하지 않는다는 것입니다. URL 구성을 망칠 수있는 많은 방법이 있지만 모든 사람이 당신처럼 훌륭하지는 않습니다. URLEncoding에 대해 SO를 찾고있는 대부분의 사용자는 아마도 " 사용자는 실제로 혼란 / 남용 "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 / a space contains.html"); "new URL ()"에서 MalformedURLException으로 폭탄이 터집니다. 이 문제를 해결하는 방법에 대한 아이디어가 있습니까?
ZioByte

다음과 같이해야합니다. String urlStr = " some / directory / spaces.html을 포함 하는 파일"; URL url = 새 URL (urlStr); URI URI = 새 URI (url.getProtocol (), url.getUserInfo (), url.getHost (), url.getPort (), url.getPath (), url.getQuery (), url.getRef ()); urlStr = uri.toASCIIString (); urlStr.replace ( "http : //", "file : ///"); 나는 그것을 테스트하지는 않았지만 그것이 효과가있을 것이라고 생각한다 .... :)
M Abdul Sami

1
@tibi uri.toString () 메소드를 사용하여 Ascii 문자열 대신 문자열로 변환 할 수 있습니다.
M Abdul Sami

1
내가 작업 한 API는 +공백을 대체 하지 않지만 % 20을 수락 하므로이 솔루션은 BalusC보다 효과적이었습니다. 감사합니다!
Julian Honma

35

1
이것들은와 같은 구피 탈출 규칙으로 고통받습니다 URLEncoder.
2rs2ts

3
그들이 문제가 있는지 확실하지 않습니다. 예를 들어 "+"또는 "% 20"을 구별 URLEncoder하지 않는 ""(form param 또는 path param)을 이스케이프 합니다.
Emmanuel Touzery

1
이것은 나를 위해 URLEncoder ()에 대한 호출을 UrlEscapers.urlFragmentEscaper ()으로 호출하기 위해 대체했으며 UrlEscapers.urlPathSegmentEscaper ()을 대신 사용 해야하는지 명확하지 않습니다.
Paul Taylor

2
URLEncoder와 달리 '+'를 인코딩하지 않기 때문에 실제로는 작동하지 않았습니다. URL +를 사용하면 URL은 홀로 남겨두고 서버는 '+'를 공간으로 디코딩하지만 URLEncoder를 사용하면 '+'는 % 2B로 변환되고 다시 +로 올바르게 디코딩됩니다.
Paul Taylor

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() NFC 인코딩 유니 코드를 퍼센트 인코딩하는 데 사용 합니다 (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$"
}

이 솔루션은 Web Plattform Tests에서 제공하는 약 100 개의 테스트 케이스를 통과 합니다.


1

안드로이드에서는이 코드를 사용합니다 :

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 (query, StandardCharsets.UTF_8.displayName ()); 또는 this : URLEncoder.encode (query, "UTF-8");
  2. 다음 코드를 사용할 수 있습니다.

    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.