HTTP URL-адрес кодирования в Java - PullRequest
350 голосов
/ 07 апреля 2009

Мое автономное Java-приложение получает от пользователя URL-адрес (который указывает на файл), и мне нужно нажать его и загрузить. Проблема, с которой я сталкиваюсь, заключается в том, что я не могу правильно закодировать URL-адрес HTTP ...

Пример:

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

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

возвращает меня:

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

Но я хочу

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

(пробел заменен на% 20)

Полагаю, URLEncoder не предназначен для кодирования URL-адресов HTTP ... JavaDoc сообщает "Класс служебных программ для кодирования форм HTML" ... Есть ли другой способ сделать это?

Ответы [ 24 ]

7 голосов
/ 04 июня 2015

Я прочитал предыдущие ответы, чтобы написать свой собственный метод, потому что я не мог правильно что-то работать, используя решение предыдущих ответов, это выглядит хорошо для меня, но если вы можете найти URL, который не работает с этим, пожалуйста, дайте мне знаю.

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", "%"));
}
7 голосов
/ 28 сентября 2010

Проблема все еще существует, если в вашем URL есть закодированный символ "/" (% 2F).

RFC 3986 - в разделе 2.2 говорится: «Если данные для компонента URI будут конфликтовать с целью зарезервированного символа в качестве разделителя, то конфликтующие данные должны быть закодированы в процентах до формирования URI». (RFC 3986 - раздел 2.2)

Но есть проблема с Tomcat:

http://tomcat.apache.org/security-6.html - Исправлено в Apache Tomcat 6.0.10

важно: Обратный путь в каталогах CVE-2007-0450

Tomcat разрешает "\", "% 2F" и "% 5C" [...].

Следующие системные свойства Java были добавлены в Tomcat, чтобы обеспечить дополнительный контроль обработки разделители пути в URL (оба варианта по умолчанию false):

  • org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: правда | ложь
  • org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH: правда | ложь

из-за невозможности гарантировать что все URL обрабатываются Tomcat как они находятся в прокси-серверах Tomcat всегда должен быть защищен, как будто нет прокси, ограничивающий доступ к контексту, был б.

Влияет: 6.0.0-6.0.9

Так что, если у вас есть URL с символом% 2F, Tomcat возвращает: «400 Invalid URI: noSlash»

Вы можете переключить исправление в скрипте запуска Tomcat:

set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%   -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true 
4 голосов
/ 04 июня 2011

Я согласен с Мэттом. На самом деле, я никогда не видел, чтобы это было хорошо объяснено в руководствах, но один вопрос заключается в том, как кодировать путь URL, а совсем другой вопрос - в том, как кодировать параметры, которые добавляются к URL (часть запроса, за "?" " условное обозначение). Они используют похожую кодировку, но не одинаковую.

Специально для кодировки символа пробела. Путь URL должен быть закодирован как% 20, тогда как часть запроса допускает% 20, а также знак «+». Лучше всего протестировать его самостоятельно на нашем веб-сервере с помощью веб-браузера.

В обоих случаях I ВСЕГДА будет кодировать КОМПОНЕНТ ПО КОМПОНЕНТУ , а не всю строку. Действительно, URLEncoder допускает это для части запроса. Для части пути вы можете использовать класс URI, хотя в этом случае он запрашивает всю строку, а не один компонент.

В любом случае, я считаю, что лучший способ избежать этих проблем - это использовать личный неконфликтный дизайн. Как? Например, я никогда не назову каталоги или параметры, используя символы, отличные от a-Z, A-Z, 0-9 и _. Таким образом, единственной необходимостью является кодирование значения каждого параметра, так как оно может быть получено из пользовательского ввода, а используемые символы неизвестны.

3 голосов
/ 18 мая 2016

Вы также можете использовать GUAVA и путь к эскаперу: UrlEscapers.urlFragmentEscaper().escape(relativePath)

3 голосов
/ 14 марта 2013

Возможно, можете попробовать UriUtils в org.springframework.web.util

UriUtils.encodeUri(input, "UTF-8")
2 голосов
/ 29 июля 2011

В дополнение к ответу Карлоса Хойбергера: если требуется значение, отличное от значения по умолчанию (80), следует использовать конструктор 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 голосов
/ 08 августа 2018

Я взял содержимое выше и немного изменил его. Сначала мне нравится позитивная логика, и я подумал, что HashSet может дать лучшую производительность, чем некоторые другие опции, такие как поиск по строке. Хотя я не уверен, стоит ли штраф за автобокс, но если компилятор оптимизирует ASCII-символы, тогда стоимость бокса будет низкой.

/***
 * 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 голос
/ 12 апреля 2018

Используйте следующее стандартное решение Java (проходит около 100 тестовых случаев, предоставленных Web Plattform Tests ):

0. Проверка, если URL уже закодирован .

1. Разделить URL на структурные части. Используйте java.net.URL для этого.

2. Правильно закодируйте каждую деталь конструкции!

3. Используйте от IDN.toASCII(putDomainNameHere) до Punycode кодируйте имя хоста!

4. Используйте java.net.URI.toASCIIString() для процентного кодирования, кодированного в NFC юникода - (лучше будет NFKC!).

Найти больше здесь: https://stackoverflow.com/a/49796882/1485527

0 голосов
/ 07 июня 2013

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

Полагаю, это будет константа, и только имя файла изменяется димически, поэтому получите имя файла

Строка имени файла; // получаем имя файла

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

0 голосов
/ 20 марта 2012

Как насчет:

public String UrlEncode (String in_) {

String retVal = "";

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

return retVal;

}

...