Pregunta ¿Cómo decodifico correctamente los parámetros Unicode pasados ​​a un servlet?


Supongamos que tengo:

<a href="http://www.yahoo.com/" 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>

he confirmado external_title se codifica como Yahoo!%E2%84%A2 y pasó a SERVLET. Si en SERVLET Hago:

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

yo obtengo Yahoo! Â "¢ en el navegador. Si cambio manualmente la codificación de caracteres del navegador a UTF-8, cambia a Yahoo!TM (que es lo que quiero).

Así que pensé que la codificación que estaba enviando al navegador era incorrecta (era Content-type: text/html; charset=ISO-8859-1) Cambié SERVLET a:

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

Ahora la codificación de caracteres del navegador es UTF-8, pero produce Yahoo! Â ¢ y no puedo hacer que el navegador muestre el carácter correcto.

Mi pregunta es: ¿hay alguna combinación de Content-type y / o new String(request.getParameter("external_title").getBytes(), "UTF-8"); y / o algo más que resulte en Yahoo!TM apareciendo en el SERVLET ¿salida?


35
2018-01-22 16:39


origen


Respuestas:


Estás casi allí. EncodeURIComponent codifica correctamente en UTF-8, que es lo que siempre debería usar en una URL hoy.

El problema es que la cadena de consulta enviada queda mutilada en el script del lado del servidor, porque getParameter () usa ISO-8559-1 en lugar de UTF-8. Esto proviene de Ancient Times antes de que la web se decidiera por UTF-8 para URI / IRI, pero es bastante patético que las especificaciones de Servlet no se hayan actualizado para que coincidan con la realidad, o al menos brinden una opción fiable y compatible.

(Hay request.setCharacterEncoding en Servlet 2.3, pero no afecta el análisis de cadena de consulta, y si un parámetro único ha sido leído anteriormente, posiblemente por algún otro elemento de framework, no funcionará en absoluto).

Por lo tanto, debe utilizar los métodos específicos del contenedor para obtener el UTF-8 adecuado, que a menudo implica cosas en server.xml. Esto es una mierda para la distribución de aplicaciones web que deberían funcionar en cualquier lugar. Para Tomcat ver http://wiki.apache.org/tomcat/FAQ/CharacterEncoding y también ¿Cuál es la diferencia entre "URIEncoding" de Tomcat, filtro de codificación y request.setCharacterEncoding.


41
2018-01-22 18:36



Obtuve el mismo problema y lo resolví decodificando Request.getQueryString() usando URLDecoder (), y después de extraer mis parámetros.

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

18
2018-03-31 14:58



Hay forma de hacerlo en Java (sin jugar con server.xml)

No funcionan :

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);
// ÏηγÏÏÏÏη // !!!!!!!!!!!!!!!!!!!!!!!!!!!

Trabajos :

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

Trabajado pero se romperá si la codificación por defecto! = utf-8 - Pruebe esto en su lugar (omita la llamada para decodificar () no es necesario):

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

Como dije antes si el server.xml está en mal estado como en:

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

(Observe la URIEncoding="UTF-8") el código anterior se romperá (porque getBytes("iso-8859-1") tiene que leer getBytes("UTF-8")) Entonces, para una solución a prueba de balas, debe obtener el valor del URIEncoding atributo. Desafortunadamente, esto parece ser específico del contenedor, incluso peor versión de contenedor específico. Para Tomcat 7 necesitarías algo como:

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

Y aún necesita ajustar esto para múltiples conectores (verifique las partes comentadas). Entonces usarías algo como:

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

Todavía esto puede fallar (IIUC) Si parameter = request.getParameter("name"); decodificado con CHARSET_FOR_URI_ENCODING estaba dañado por lo que los bytes que obtengo con getBytes () no eran los originales (es por eso que "iso-8859-1" se usa de forma predeterminada - preservará los bytes) Puede deshacerse de todo analizando manualmente la cadena de consulta en las líneas de:

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

Todavía estoy buscando el lugar en los documentos donde se menciona que request.getParameter("name") llama URLDecoder.decode()  en lugar de devolver el %CF%84%CE%B7%CE%B3%CF%81%CF%84%CF%83%CF%82%CE%B7 cuerda ? Un enlace en la fuente sería muy apreciado.
Además, ¿cómo puedo pasar como el valor del parámetro la cadena, por ejemplo, %CE ? => ver comentario: parameter=%25CE


15
2017-10-06 21:04



Sospecho que la mutilación de datos ocurre en la solicitud, es decir, que la codificación declarada de la solicitud no coincide con la que realmente se usa para los datos.

Que hace request.getCharacterEncoding() ¿regreso?

Realmente no sé cómo JavaScript maneja las codificaciones o cómo hacer que use una específica.

Debe asegurarse de que las codificaciones se utilizan correctamente en todas las etapas. NO intente "arreglar" los datos mediante el uso de new String() un getBytes() en un punto donde ya ha sido codificado incorrectamente.

Editar: Puede ser útil tener la página de origen (la que tiene el Javascript) también codificada en UTF-8 y declarada como tal en su tipo de contenido. Entonces creo que Javascript puede usar el UTF-8 como opción predeterminada, pero esto no es un conocimiento definitivo, solo conjeturas.


2
2018-01-22 17:16



Siempre puedes usar javascript para manipular el texto más.

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

0
2018-01-22 17:13



Creo que puedo hacer que lo siguiente funcione:

encodeURIComponent(escape(lnk.title))

Eso me da %25u2122 (para & # 8482) o %25AE (para & # 174), que se decodificará a %u2122 y %AE respectivamente en el servlet.

Entonces debería ser capaz de convertir% u2122 en '\u2122' y% AE en '\u00AE' uso relativamente fácil (char) (base-10 integer value of %uXXXX or %XX) en un bucle de coincidencia y reemplazo utilizando expresiones regulares.

es decir, partido /%u([0-9a-f]{4})/i, extraiga la subexpresión correspondiente, conviértala a base-10, conviértala en un carácter y añádala a la salida, luego haga lo mismo con /%([0-9a-f]{2})/i


0
2018-01-22 18:22



Hay un error en ciertas versiones de Jetty que lo hace analizar incorrectamente los caracteres UTF-8 de mayor número. Si su servidor acepta letras arábigas correctamente pero no emoji, eso es una señal de que tiene una versión con este problema, ya que árabe no está en ISO-8859-1, pero está en el rango inferior de caracteres UTF-8 ("menor" que significa java lo representará en un solo char).

Actualicé de la versión 7.2.0.v20101020 a la versión 7.5.4.v20111024 y esto solucionó el problema; Ahora puedo usar el método getParameter (String) en lugar de tener que analizarlo yo mismo.

Si es realmente curioso, puede profundizar en su versión de org.eclipse.jetty.util.Utf8StringBuilder.append (byte) y ver si agrega correctamente varios caracteres a la cadena cuando el código utf-8 es lo suficientemente alto o si , como en 7.2.0, simplemente lanza un int a un char y lo agrega.


0
2017-11-18 17:16