Mã hóa địa chỉ URL HTTP trong Java


366

Ứng dụng độc lập Java của tôi nhận được một URL (trỏ đến một tệp) từ người dùng và tôi cần phải nhấn nó và tải xuống. Vấn đề tôi gặp phải là tôi không thể mã hóa địa chỉ URL HTTP đúng cách ...

Thí dụ:

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

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

trả lại cho tôi:

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

Nhưng, điều tôi muốn là

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

(không gian được thay thế bằng% 20)

Tôi đoán URLEncoderlà không được thiết kế để mã hóa URL HTTP ... JavaDoc nói "Lớp tiện ích cho mã hóa biểu mẫu HTML" ... Có cách nào khác để làm điều này không?



Hành vi hoàn toàn chính xác. Mã hóa URL là để biến một cái gì đó thành một chuỗi có thể được chuyển một cách an toàn dưới dạng tham số URL và hoàn toàn không được hiểu là một URL. Trong khi đó bạn muốn nó chỉ chuyển đổi một phần nhỏ của URL.
Stephen Holt

Câu trả lời:


303

Lớp java.net.URI có thể giúp đỡ; trong tài liệu về URL bạn tìm thấy

Lưu ý, lớp URI thực hiện thoát các trường thành phần của nó trong các trường hợp nhất định. Cách được đề xuất để quản lý mã hóa và giải mã URL là sử dụng URI

Sử dụng một trong các hàm tạo có nhiều hơn một đối số, như:

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

(hàm tạo đối số đơn của URI KHÔNG thoát các ký tự không hợp lệ)


Chỉ những nhân vật bất hợp pháp mới thoát được mã trên - nó không thoát khỏi các ký tự không phải ASCII (xem bình luận của fatih).
Các toASCIIStringphương pháp có thể được sử dụng để có được một chuỗi duy nhất với các nhân vật US-ASCII:

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

Đối với một URL có truy vấn như thế http://www.google.com/ig/api?weather=São Paulo, hãy sử dụng phiên bản 5 tham số của hàm tạo:

URI uri = new URI(
        "http", 
        "www.google.com", 
        "/ig/api",
        "weather=São Paulo",
        null);
String request = uri.toASCIIString();

13
Xin lưu ý, lớp URI được đề cập ở đây là từ "org.apache.commons.httpclient.URI" không phải "java.net", "java.net" không URI không chấp nhận các ký tự không hợp lệ, trừ khi bạn sẽ sử dụng các nhà xây dựng xây dựng URL từ các thành phần của nó, giống như cách được đề cập trong bình luận của Matt bên dưới
Mohamed Faramawi

7
@Mohamed: lớp tôi đã đề cập và sử dụng để thử nghiệm thực sự là java.net.URI : nó hoạt động hoàn hảo (Java 1.6). Tôi sẽ đề cập đến tên lớp đủ điều kiện nếu nó không phải là tên Java tiêu chuẩn và các liên kết trỏ đến tài liệu của java.net.URI. Và, bằng nhận xét của Sudhakar, nó đã giải quyết vấn đề mà không bao gồm bất kỳ "thư viện commons" nào!
dùng85421

1
URI uri = URI mới ("http", "search.barnesandnoble.com", "/ Booksearch / é", null); Không thoát đúng với mẫu này? Điều này đáng lẽ phải được thoát với% thoát
fmucar

@fatih - đúng rồi, cảm ơn! Thông thường đó không phải là một vấn đề, nhưng có một giải pháp đơn giản - gần giống như tôi đã viết trước đây. Xem chỉnh sửa thứ 2.
dùng85421

@Carlos Thx để chỉnh sửa. Bây giờ nó thoát nhưng không thoát được. Nên thêm% vào giá trị HEX của char cho tham số Path có nghĩa là é char nên được chuyển đổi thành%
e9

91

Xin được cảnh báo rằng hầu hết các câu trả lời ở trên là TUYỆT VỜI.

Các URLEncoderlớp, mặc dù là tên, là KHÔNG những gì cần phải có mặt ở đây. Thật không may khi Sun đặt tên lớp học này rất khó chịu. URLEncodercó nghĩa là để truyền dữ liệu dưới dạng tham số, không phải để mã hóa chính URL.

Nói cách khác, "http://search.barnesandnoble.com/booksearch/first book.pdf"là URL. Các thông số sẽ là, ví dụ , "http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that". Các tham số là những gì bạn sẽ sử dụng URLEncodercho.

Hai ví dụ sau đây nêu bật sự khác biệt giữa hai.

Sau đây tạo ra các tham số sai, theo tiêu chuẩn HTTP. Lưu ý ký hiệu (&) và dấu cộng (+) được mã hóa không chính xác.

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

Sau đây sẽ tạo ra các tham số chính xác, với truy vấn được mã hóa chính xác. Lưu ý các khoảng trắng, ký hiệu và dấu cộng.

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

2
Đúng vậy, hàm tạo URI đã mã hóa chuỗi truy vấn, theo tài liệu 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
@Draemon Câu trả lời là đúng nhưng sử dụng chuỗi truy vấn theo cách không phổ biến; một ví dụ bình thường hơn có thể là query = URLEncoder.encode(key) + "=" + URLEncoder.encode(value). Các tài liệu chỉ nói rằng "bất kỳ ký tự nào không phải là ký tự URI hợp pháp đều được trích dẫn".
tc.

1
Tôi đồng ý với Matt ở đây. Nếu bạn nhập URL này: " google.com/help/me/book name + me /? MY CRZY QUERY! + & + :)" trong trình duyệt, nó sẽ tự động mã hóa khoảng trắng nhưng "&" được sử dụng làm giá trị truy vấn dấu phân cách và "+" bị mất.
arcot

80

Tôi sẽ thêm một đề xuất ở đây nhằm vào người dùng Android. Bạn có thể làm điều này để tránh phải có bất kỳ thư viện bên ngoài. Ngoài ra, tất cả các giải pháp tìm kiếm / thay thế các ký tự được đề xuất trong một số câu trả lời ở trên đều nguy hiểm và nên tránh.

Hãy thử xem:

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

Bạn có thể thấy rằng trong URL cụ thể này, tôi cần mã hóa các khoảng trắng đó để tôi có thể sử dụng nó cho một yêu cầu.

Điều này tận dụng một vài tính năng có sẵn cho bạn trong các lớp Android. Đầu tiên, lớp URL có thể ngắt một url thành các thành phần thích hợp của nó để bạn không cần phải thực hiện bất kỳ công việc tìm kiếm / thay thế chuỗi nào. Thứ hai, cách tiếp cận này tận dụng tính năng của lớp URI của các thành phần thoát đúng khi bạn xây dựng một URI thông qua các thành phần thay vì từ một chuỗi.

Cái hay của phương pháp này là bạn có thể lấy bất kỳ chuỗi url hợp lệ nào và để nó hoạt động mà không cần bất kỳ kiến ​​thức đặc biệt nào về nó.


3
Cách tiếp cận tuyệt vời, nhưng tôi muốn chỉ ra rằng mã này không ngăn chặn mã hóa kép , ví dụ% 20 được mã hóa thành% 2520. Câu trả lời của Scott không phải chịu đựng điều này.
Nattster

2
Nó không thể xử lý #.
Alston

Hoặc nếu bạn chỉ muốn thực hiện trích dẫn đường dẫn: URI mới (null, null, "/ path with space", null, null) .toString ()
user1050755

1
@Stallman Nếu tên tệp của bạn chứa #, lớp URL sẽ đặt nó vào "ref" (tương đương với "đoạn" trong lớp URI). Bạn có thể phát hiện xem URL.getRef () có trả về thứ gì đó có thể được coi là một phần của đường dẫn hay không và truyền URL.getPath () + "#" + URL.getRef () làm tham số "đường dẫn" và null là "đoạn" "Tham số của hàm tạo tham số lớp 7 URI. Theo mặc định, chuỗi sau # được coi là tham chiếu (hoặc neo).
gouliej

49

một giải pháp tôi đã phát triển và ổn định hơn nhiều so với bất kỳ giải pháp nào khác:

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

}

3
điều đó cũng đòi hỏi bạn phải chia url thành từng mảnh. Không có cách nào để máy tính biết phần nào của url để mã hóa. Xem chỉnh sửa ở trên của tôi
fmucar

4
@fmucar Cảm ơn vì đoạn mã đó! Cần lưu ý rằng đây không phải là UTF-8. Để nhận UTF-8, chỉ cần xử lý trước đầu vào bằng String utf8Input = new String(Charset.forName("UTF-8").encode(input).array());(lấy từ đây )
letmaik

1
Giải pháp này thực sự cũng sẽ mã hóa phần "http: //" thành "http% 3A% 2F% 2F", đây là điều mà câu hỏi ban đầu đã cố gắng tránh.
Benjamin Piette

2
Bạn chỉ vượt qua những gì bạn cần mã hóa, không phải toàn bộ URL. Không có cách nào để vượt qua toàn bộ một chuỗi URL và mong muốn mã hóa chính xác. Trong mọi trường hợp, bạn cần chia url thành các phần logic của nó.
fmucar

2
Tôi gặp vấn đề với câu trả lời này vì nó không mã hóa các ký tự không an toàn cho UTF-8 .. có thể phụ thuộc vào ứng dụng ngang hàng.
Tarnschaf

36

Nếu bạn có một URL, bạn có thể chuyển url.toString () vào phương thức này. Giải mã đầu tiên, để tránh mã hóa kép (ví dụ: mã hóa một khoảng trắng dẫn đến% 20 và mã hóa kết quả ký hiệu phần trăm theo% 25, ​​vì vậy mã hóa kép sẽ biến một khoảng trắng thành% 2520). Sau đó, sử dụng URI như được giải thích ở trên, thêm vào tất cả các phần của URL (để bạn không bỏ tham số truy vấn).

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

1
URLDecoder.decode (chuỗi, "UTF-8") không thành công với IllegalArgumentException khi bạn chuyển chuỗi dưới dạng " google.co.in/search?q=123%!123 ". Đây là một URL hợp lệ. Tôi đoán API này không hoạt động khi% được sử dụng làm dữ liệu thay vì ký tự mã hóa.
MediumOne

26

Phải mã hóa URL sẽ mã hóa chuỗi đó để nó được truyền đúng trong một url đến đích cuối cùng. Ví dụ: bạn không thể có http://stackoverflow.com?url=http://yyy.com . UrlEncoding tham số sẽ sửa giá trị tham số đó.

Vì vậy, tôi có hai lựa chọn cho bạn:

  1. Bạn có quyền truy cập vào đường dẫn riêng biệt với tên miền? Nếu vậy, bạn có thể chỉ cần UrlEncode đường dẫn. Tuy nhiên, nếu đây không phải là trường hợp thì tùy chọn 2 có thể dành cho bạn.

  2. Nhận commons-httpclient-3.1. Điều này có một lớp URIUtil:

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

Điều này sẽ xuất ra chính xác những gì bạn đang tìm kiếm, vì nó sẽ chỉ mã hóa phần đường dẫn của URI.

FYI, bạn sẽ cần commons-codec và commons-log để phương thức này hoạt động trong thời gian chạy.


Sidenote apache commons đã ngừng duy trì URIUtil trong các nhánh 4.x, khuyên bạn nên sử dụng lớp URI của JDK thay thế. Chỉ có nghĩa là bạn phải tự phá vỡ chuỗi.
Nicholi

2) Chính xác thì nó cũng được đề xuất ở đây stackoverflow.com/questions/5330104/ tôi cũng đã sử dụng URIUtilgiải pháp
To Kra

11

Nitpicking: một chuỗi chứa một ký tự khoảng trắng theo định nghĩa không phải là URI. Vì vậy, những gì bạn đang tìm kiếm là mã thực hiện thoát URI được xác định trong Mục 2.1 của RFC 3986 .


Chúng ta cần "làm thế nào" trong câu trả lời, không phải "cái gì".
shinzou

11

Thật không may, org.apache.commons.httpclient.util.URIUtilkhông được chấp nhận và replacement org.apache.commons.codec.net.URLCodecmã hóa phù hợp cho các bài đăng mẫu, không phải trong URL thực tế. Vì vậy, tôi đã phải viết hàm riêng của mình, một thành phần duy nhất (không phù hợp với toàn bộ chuỗi truy vấn có? Và & '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);
  }
}

Thôi nào, phải có một thư viện làm việc này.
shinzou

9

URLEncoding có thể mã hóa các URL HTTP tốt, như bạn không may phát hiện ra. Chuỗi bạn đã truyền vào, " http://search.barnesandnoble.com/booksearch/first book.pdf", được mã hóa chính xác và hoàn toàn thành một dạng được mã hóa URL. Bạn có thể chuyển toàn bộ chuỗi gobbledigook dài mà bạn đã lấy lại dưới dạng tham số trong URL và nó có thể được giải mã trở lại chính xác chuỗi bạn đã truyền.

Có vẻ như bạn muốn làm một cái gì đó khác một chút so với việc chuyển toàn bộ URL dưới dạng tham số. Từ những gì tôi thu thập được, bạn đang cố gắng tạo một URL tìm kiếm trông giống như " http://search.barnesandnoble.com/booksearch/whthingTheUserPassesIn ". Điều duy nhất bạn cần mã hóa là bit "anythingTheUserPassesIn", vì vậy có lẽ tất cả những gì bạn cần làm là một cái gì đó như thế này:

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

Điều đó sẽ tạo ra một cái gì đó hợp lệ hơn cho bạn.


17
Điều đó sẽ thay thế khoảng trắng trong userInput bằng "+". Người đăng cần họ thay thế bằng "% 20".
vocaro

@vocaro: đó là một điểm rất tốt. URLEncoder thoát như các đối số là các tham số truy vấn, không giống như phần còn lại của URL.
Brandon Yarbrough

9

Nếu bất cứ ai không muốn thêm một phụ thuộc vào dự án của họ, các chức năng này có thể hữu ích.

Chúng tôi chuyển phần 'đường dẫn' của URL vào đây. Bạn có thể không muốn truyền URL đầy đủ dưới dạng tham số (chuỗi truy vấn cần các lối thoát khác nhau, v.v.).

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

Và kiểm tra:

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

}

Cảm ơn vì điều này, nhưng tôi cần phải làm gì để mã hóa một khoảng trắng -> sử dụng% 20 thay cho ví dụ của bạn?
N00b Pr0grammer

Được cập nhật vào tài khoản cho các không gian dưới dạng% 20
Cuga

7

Vẫn còn một vấn đề nếu bạn đã mã hóa "/" (% 2F) trong URL của mình.

RFC 3986 - Phần 2.2 nói: "Nếu dữ liệu cho thành phần URI sẽ xung đột với mục đích của một ký tự dành riêng là dấu phân cách, thì dữ liệu xung đột phải được mã hóa theo phần trăm trước khi URI được hình thành." (RFC 3986 - Mục 2.2)

Nhưng có một vấn đề với Tomcat:

http://tomcat.apache.org/security-6.html - Đã sửa lỗi trong Apache Tomcat 6.0.10

quan trọng: Thư mục truyền tải CVE-2007-0450

Tomcat cho phép '\', '% 2F' và '% 5C' [...].

Các thuộc tính hệ thống Java sau đây đã được thêm vào Tomcat để cung cấp thêm quyền kiểm soát việc xử lý các dấu phân cách đường dẫn trong URL (cả hai tùy chọn mặc định là false):

  • org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: đúng | sai
  • org.apache.cirthina.connector.CoyoteAd Module.ALLOW_BACKSLASH: true | false

Do không thể đảm bảo rằng tất cả các URL được Tomcat xử lý khi chúng ở trong các máy chủ proxy, Tomcat phải luôn được bảo mật như thể không sử dụng proxy hạn chế truy cập ngữ cảnh.

Ảnh hưởng: 6.0.0-6.0.9

Vì vậy, nếu bạn đã có một URL với ký tự% 2F, Tomcat trả về: "400 URI không hợp lệ: noSlash"

Bạn có thể chuyển đổi lỗi trong tập lệnh khởi động Tomcat:

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

7

Tôi đọc các câu trả lời trước để viết phương pháp của riêng mình vì tôi không thể có một cái gì đó hoạt động chính xác bằng cách sử dụng giải pháp của các câu trả lời trước, nó có vẻ tốt cho tôi nhưng nếu bạn có thể tìm thấy URL không hoạt động với điều này, vui lòng cho tôi biết.

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

4

Tôi đồng ý với Matt. Thật vậy, tôi chưa bao giờ thấy nó được giải thích rõ trong các hướng dẫn, nhưng một vấn đề là làm thế nào để mã hóa đường dẫn URL và một điều rất khác là làm thế nào để mã hóa các tham số được gắn vào URL (phần truy vấn, đằng sau dấu "? " Biểu tượng). Họ sử dụng mã hóa tương tự, nhưng không giống nhau.

Đặc biệt cho mã hóa của ký tự khoảng trắng. Đường dẫn URL cần được mã hóa thành% 20, trong khi phần truy vấn cho phép% 20 và cả dấu "+". Ý tưởng tốt nhất là tự kiểm tra nó với máy chủ Web của chúng tôi, bằng trình duyệt Web.

Đối với cả hai trường hợp, tôi LUÔN sẽ mã hóa THÀNH PHẦN B COMPNG THÀNH PHẦN , không bao giờ là toàn bộ chuỗi. Thật vậy, URLEncoder cho phép phần truy vấn đó. Đối với phần đường dẫn, bạn có thể sử dụng URI lớp, mặc dù trong trường hợp này, nó yêu cầu toàn bộ chuỗi, không phải là một thành phần.

Dù sao, tôi tin rằng cách tốt nhất để tránh những vấn đề này là sử dụng một thiết kế cá nhân không xung đột. Làm sao? Ví dụ: tôi không bao giờ đặt tên thư mục hoặc tham số bằng các ký tự khác ngoài aZ, AZ, 0-9 và _. Theo cách đó, nhu cầu duy nhất là mã hóa giá trị của mọi tham số, vì nó có thể đến từ đầu vào của người dùng và các ký tự được sử dụng là không xác định.


2
mã mẫu sử dụng URL trong câu hỏi sẽ là một điều tốt để đưa vào câu trả lời của bạn
Martin Serrano


3

Bạn cũng có thể sử dụng GUAVAvà đường dẫn thoát: UrlEscapers.urlFragmentEscaper().escape(relativePath)


2

Ngoài câu trả lời của Carlos Heuberger: nếu cần một giá trị khác với mặc định (80), thì nên sử dụng hàm tạo 7 param:

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

2

Tôi lấy nội dung trên và thay đổi nó một chút. Tôi thích logic tích cực trước tiên và tôi nghĩ rằng Hashset có thể cho hiệu suất tốt hơn một số tùy chọn khác, như tìm kiếm thông qua Chuỗi. Mặc dù, tôi không chắc hình phạt tự động có đáng hay không, nhưng nếu trình biên dịch tối ưu hóa cho ký tự ASCII, thì chi phí cho quyền anh sẽ thấp.

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

1

Sử dụng giải pháp Java tiêu chuẩn sau (vượt qua khoảng 100 trong số các thử nghiệm được cung cấp bởi các thử nghiệm Plattform Web ):

0. Kiểm tra nếu URL đã được mã hóa .

1. Chia URL thành các phần cấu trúc. Sử dụng java.net.URL cho nó.

2. Mã hóa từng phần cấu trúc đúng cách!

3. Sử dụng IDN.toASCII(putDomainNameHere)để Punycode mã hóa tên máy chủ!

4. Sử dụng java.net.URI.toASCIIString()để mã hóa phần trăm, mã hóa NFC được mã hóa - (tốt hơn sẽ là NFKC!).

Tìm thêm tại đây: https://stackoverflow.com/a/49796882/1485527


0

Tôi đã tạo một dự án mới để giúp xây dựng các URL HTTP. Thư viện sẽ tự động mã hóa các đoạn đường dẫn URL và các tham số truy vấn.

Bạn có thể xem nguồn và tải xuống tệp nhị phân tại https://github.com/Widen/urlbuilder

URL ví dụ trong câu hỏi này:

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

sản xuất

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


0

Tôi đã từng gặp vấn đề tương tự. Đã giải quyết điều này bằng cách bỏ chọn:

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

Nó mã hóa chuỗi nhưng bỏ qua ":" và "/".


0

tôi dùng cái này

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

thêm phần phụ thuộc này

 <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>1.8</version>
    </dependency>

-2

Tôi phát triển một thư viện phục vụ mục đích này: galimatias . Nó phân tích cú pháp URL giống như cách các trình duyệt web làm. Đó là, nếu một URL hoạt động trong một trình duyệt, nó sẽ được phân tích cú pháp chính xác bởi các galimatias .

Trong trường hợp này:

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

Sẽ cung cấp cho bạn : http://search.barnesandnoble.com/booksearch/first%20book.pdf. Tất nhiên đây là trường hợp đơn giản nhất, nhưng nó sẽ hoạt động với mọi thứ, vượt xa java.net.URI.

Bạn có thể kiểm tra xem tại: https://github.com/smola/galimatias


-3

Bạn có thể sử dụng một chức năng như thế này. Hoàn thành và sửa đổi nó theo nhu cầu của bạn:

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

Ví dụ sử dụng:

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

Kết quả là: http://www.growup.com/folder/int%C3%A9rieur-%C3%A0_vendre?o=4


1
Câu trả lời này không đầy đủ nếu không có URLCodec.
Hầu tước Lorne

upvote cho chuỗi .replace (), nó không lý tưởng nhưng nó đủ cho các trường hợp sử dụng ad-hoc cơ bản
svarog

-5

Chuỗi url = "" http://search.barnesandnoble.com/booksearch/ ;

Đây sẽ là hằng số tôi đoán và chỉ tên tệp thay đổi một cách ngẫu nhiên để có được tên tệp

Tên tệp chuỗi; // lấy tên tập tin

Chuỗi urlEnc = url + fileName.replace ("", "% 20");


2
Còn tất cả các nhân vật bất hợp pháp khác thì sao?
Hầu tước Lorne

-7

Làm thế nào về:

Chuỗi UrlEncode công khai (Chuỗi in_) {

String retVal = "";

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

return retVal;

}


URLEncoder không thể được sử dụng để thoát các ký tự URL ivalid. Chỉ để mã hóa các hình thức.
Cung thủ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.