Подтвердить что ты не робот

Как правильно декодировать параметры юникода, переданные в сервлет

Предположим, что у меня есть:

<a href="#" onclick="location.href='http://www.yahoo.com/'; return false;" target="_yahoo" 
    title="Yahoo!&#8482;" onclick="return gateway(this);">Yahoo!</a>
<script type="text/javascript">
function gateway(lnk) {
    window.open(SERVLET +
        '?external_link=' + encodeURIComponent(lnk.href) +
        '&external_target=' + encodeURIComponent(lnk.target) +
        '&external_title=' + encodeURIComponent(lnk.title));
    return false;
}
</script>

Я подтвердил, что external_title закодирован как Yahoo!%E2%84%A2 и передан в SERVLET. Если в SERVLET я делаю:

Writer writer = response.getWriter();
writer.write(request.getParameter("external_title"));

Я получаю Yahoo! â "¢ в браузере. Если я вручную переключу кодировку символов браузера на UTF-8, она изменится на Yahoo! TM (это то, что я хочу).

Итак, я понял, что кодировка, которую я отправлял в браузер, была неправильной (это было Content-type: text/html; charset=ISO-8859-1). Я изменил SERVLET на:

response.setContentType("text/html; charset=utf-8");
Writer writer = response.getWriter();
writer.write(request.getParameter("external_title"));

Теперь кодировка символов браузера - UTF-8, но она выводит Yahoo!, и я не могу заставить браузер отображать правильный символ вообще.

Мой вопрос: есть ли какая-то комбинация Content-type и/или new String(request.getParameter("external_title").getBytes(), "UTF-8"); и/или что-то еще, что приведет к появлению Yahoo! TM в выводе SERVLET?

4b9b3361

Ответ 1

Ты почти там. EncodeURIComponent правильно кодирует UTF-8, что вы всегда должны использовать в URL-адресе сегодня.

Проблема заключается в том, что представленная строка запроса искажается на пути к серверной стороне script, поскольку getParameter() использует ISO-8559-1 вместо UTF-8. Это происходит из Ancient Times до того, как веб-сайт установил UTF-8 для URI/IRI, но довольно жалко, что спецификация Servlet не обновлена ​​в соответствии с реальностью или, по крайней мере, обеспечивает надежный, поддерживаемый вариант для нее.

(В Servlet 2.3 есть request.setCharacterEncoding, но это не влияет на синтаксический анализ строки запроса, и если один параметр был прочитан раньше, возможно, каким-то другим элементом структуры, он вообще не работает.)

Итак, вам нужно работать с определенными контейнерами методами, чтобы получить правильный UTF-8, часто включающий материал в server.xml. Это полностью засасывает распространение веб-приложений, которые должны работать в любом месте. Для Tomcat см. http://wiki.apache.org/tomcat/FAQ/CharacterEncoding, а также В чем разница между "URIEncoding" от Tomcat, Encoding Filter и request.setCharacterEncoding.

Ответ 2

У меня возникла такая же проблема и решена ее путем декодирования Request.getQueryString() с помощью URLDecoder() и после извлечения моих параметров.

String[] Parameters = URLDecoder.decode(Request.getQueryString(), 'UTF-8')
                       .splitat('&');

Ответ 3

Есть способ сделать это в java (без возиться с server.xml)

Не работает:

protected static final String CHARSET_FOR_URL_ENCODING = "UTF-8";

String uname = request.getParameter("name");
System.out.println(uname);
// ÏηγÏÏÏÏη
uname = request.getQueryString();
System.out.println(uname);
// name=%CF%84%CE%B7%CE%B3%CF%81%CF%84%CF%83%CF%82%CE%B7
uname = URLDecoder.decode(request.getParameter("name"),
        CHARSET_FOR_URL_ENCODING);
System.out.println(uname);
// ÏηγÏÏÏÏη // !!!!!!!!!!!!!!!!!!!!!!!!!!!
uname = URLDecoder.decode(
        "name=%CF%84%CE%B7%CE%B3%CF%81%CF%84%CF%83%CF%82%CE%B7",
        CHARSET_FOR_URL_ENCODING);
System.out.println("query string decoded : " + uname);
// query string decoded : name=τηγρτσςη
uname = URLDecoder.decode(new String(request.getParameter("name")
        .getBytes()), CHARSET_FOR_URL_ENCODING);
System.out.println(uname);
// ÏηγÏÏÏÏη // !!!!!!!!!!!!!!!!!!!!!!!!!!!

Работы:

final String name = URLDecoder
        .decode(new String(request.getParameter("name").getBytes(
                "iso-8859-1")), CHARSET_FOR_URL_ENCODING);
System.out.println(name);
// τηγρτσςη

Работает, но сломается, если кодировка по умолчанию!= utf-8 - попробуйте это вместо этого (оставьте вызов для декодирования() он не нужен):

final String name = new String(request.getParameter("name").getBytes("iso-8859-1"),
        CHARSET_FOR_URL_ENCODING);

Как я сказал выше, если server.xml запутался, как в:

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1"
                     redirectPort="8443"  URIEncoding="UTF-8"/> 

(обратите внимание на URIEncoding="UTF-8"), код выше сломается (причина getBytes("iso-8859-1") должна быть прочитана getBytes("UTF-8")). Поэтому для пуленепробиваемого решения вам нужно получить значение атрибута URIEncoding. К сожалению, это, по-видимому, специфично для контейнеров - даже хуже, чем конкретная версия контейнера. Для tomcat 7 вам нужно что-то вроде:

import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;

import org.apache.catalina.Server;
import org.apache.catalina.Service;
import org.apache.catalina.connector.Connector;

public class Controller extends HttpServlet {

    // ...
    static String CHARSET_FOR_URI_ENCODING; // the `URIEncoding` attribute
    static {
        MBeanServer mBeanServer = MBeanServerFactory.findMBeanServer(null).get(
            0);
        ObjectName name = null;
        try {
            name = new ObjectName("Catalina", "type", "Server");
        } catch (MalformedObjectNameException e1) {
            e1.printStackTrace();
        }
        Server server = null;
        try {
            server = (Server) mBeanServer.getAttribute(name, "managedResource");
        } catch (AttributeNotFoundException | InstanceNotFoundException
                | MBeanException | ReflectionException e) {
            e.printStackTrace();
        }
        Service[] services = server.findServices();
        for (Service service : services) {
            for (Connector connector : service.findConnectors()) {
                System.out.println(connector);
                String uriEncoding = connector.getURIEncoding();
                System.out.println("URIEncoding : " + uriEncoding);
                boolean use = connector.getUseBodyEncodingForURI();
                // TODO : if(use && connector.get uri enc...)
                CHARSET_FOR_URI_ENCODING = uriEncoding;
                // ProtocolHandler protocolHandler = connector
                // .getProtocolHandler();
                // if (protocolHandler instanceof Http11Protocol
                // || protocolHandler instanceof Http11AprProtocol
                // || protocolHandler instanceof Http11NioProtocol) {
                // int serverPort = connector.getPort();
                // System.out.println("HTTP Port: " + connector.getPort());
                // }
            }
        }
    }
}

И все же вам нужно настроить его для нескольких разъемов (проверьте закомментированные части). Тогда вы будете использовать что-то вроде:

new String(parameter.getBytes(CHARSET_FOR_URI_ENCODING), CHARSET_FOR_URL_ENCODING);

Однако это может завершиться неудачно (IIUC), если parameter = request.getParameter("name");, декодированный с CHARSET_FOR_URI_ENCODING, был поврежден, поэтому байты, которые я получаю с getBytes(), не были оригинальными (вот почему "iso-8859-1" используется по умолчанию - он сохранит байты). Вы можете избавиться от всего этого, вручную разобрав строку запроса в строках:

URLDecoder.decode(request.getQueryString().split("=")[1],
        CHARSET_FOR_URL_ENCODING);

Я все еще ищу место в документах, где упоминается, что request.getParameter("name") вызывает URLDecoder.decode() вместо возврата строки %CF%84%CE%B7%CE%B3%CF%81%CF%84%CF%83%CF%82%CE%B7? Ссылка на источник будет очень оценена. Также как передать значение параметра, например, %CE? = > , см. комментарий: parameter=%25CE

Ответ 4

Я подозреваю, что искажение данных происходит в запросе, то есть заявленная кодировка запроса не соответствует той, которая фактически используется для данных.

Что возвращает request.getCharacterEncoding()?

Я действительно не знаю, как JavaScript обрабатывает кодировки или как использовать конкретную.

Вам нужно убедиться, что кодировки используются правильно на всех этапах - НЕ пытайтесь "исправить" данные с помощью new String() a getBytes() в том месте, где оно уже было закодировано неправильно.

Изменить: Это может помочь получить исходную страницу (ту, что с Javascript), также закодированную в UTF-8 и объявленную как таковой в своем Content-Type. Тогда я считаю, что Javascript может по умолчанию использовать UTF-8 для своего запроса - но это не определенные знания, просто догадки.

Ответ 5

Вы всегда можете использовать javascript для дальнейшего управления текстом.

<div id="test">a</div>
<script>
var a = document.getElementById('test');
alert(a.innerHTML);
a.innerHTML = decodeURI("Yahoo!%E2%84%A2");
alert(a.innerHTML);
</script>

Ответ 6

Думаю, я могу заставить следующее работать:

encodeURIComponent(escape(lnk.title))

Это дает мне %25u2122 (для & # 8482) или %25AE (для & # 174), который будет декодироваться до %u2122 и %AE соответственно в сервлете.

Затем я мог бы превратить% u2122 в '\u2122' и% AE в '\u00AE' относительно легко, используя (char) (base-10 integer value of %uXXXX or %XX) в цикле соответствия и замены, используя регулярные выражения.

то есть. - сопоставить /%u([0-9a-f]{4})/i, извлечь соответствующее подвыражение, преобразовать его в base-10, превратить его в char и добавить его к выходу, а затем сделать то же самое с /%([0-9a-f]{2})/i

Ответ 7

В некоторых версиях Jetty есть ошибка, из-за которой он неправильно анализирует символы UTF-8 большего числа. Если ваш сервер правильно принимает арабские буквы, но не emoji, это знак у вас есть версия с этой проблемой, так как арабский не находится в ISO-8859-1, но находится в нижнем диапазоне символов UTF-8 ( "нижнее" значение java будет представлять его в одном char).

Я обновил версию 7.2.0.v20101020 до версии 7.5.4.v20111024, и это устранило проблему; Теперь я могу использовать метод getParameter (String) вместо того, чтобы самостоятельно его анализировать.

Если вам действительно интересно, вы можете вставить свою версию org.eclipse.jetty.util.Utf8StringBuilder.append(byte) и посмотреть, правильно ли она добавляет несколько символов в строку, когда код utf-8 высок достаточно или если, как и в 7.2.0, он просто переводит int в char и добавляет.

Ответ 8

Спасибо за все, что я узнаю о кодировке декодирования набора символов по умолчанию, который используется в tomcat, jetty. Я использую этот метод для решения своих проблем с помощью Google Guava.

        String str = URLDecoder.decode(request.getQueryString(), StandardCharsets.UTF_8.name());
        final Map<String, String> map = Splitter.on('&').trimResults().withKeyValueSeparator("=").split(str);
        System.out.println(map);
        System.out.println(map.get("aung"));
        System.out.println(map.get("aa"));