Java 如何正确解码传递给servlet的unicode参数

Java 如何正确解码传递给servlet的unicode参数,java,unicode,servlets,Java,Unicode,Servlets,假设我有: <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_lin

假设我有:

<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>
我得到雅虎!在浏览器中。如果我手动将浏览器字符编码切换为UTF-8,它将更改为Yahoo!TM(这就是我想要的)

因此我认为我发送到浏览器的编码是错误的(它是
内容类型: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!而且我根本无法让浏览器呈现正确的字符


我的问题是:
内容类型
和/或
新字符串(request.getParameter(“external_title”).getBytes(),“UTF-8”)是否存在某种组合和/或其他将导致Yahoo!TM出现在
SERVLET
输出中?

您始终可以使用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>
a
var a=document.getElementById('test');
警报(a.innerHTML);
a、 innerHTML=decodeURI(“雅虎!%E2%84%A2”);
警报(a.innerHTML);

我怀疑请求中发生了数据损坏,即请求的声明编码与实际用于数据的编码不匹配

请求.getCharacterEncoding()
返回什么

我真的不知道JavaScript如何处理编码,也不知道如何让它使用特定的编码

您需要确保在所有阶段都正确使用编码-不要试图在数据已被错误编码的点使用
newstring()
an
getBytes()
来“修复”数据


编辑:将源页面(带有Javascript的页面)也编码为UTF-8并在其内容类型中声明为UTF-8可能会有所帮助。然后,我相信Javascript可能会默认使用UTF-8进行请求-但这不是确切的知识,只是猜测。

我想我可以做到以下几点:

encodeURIComponent(escape(lnk.title))
这给了我
%25u2122
(用于™)或
%25AE
(用于®),它们将分别在servlet中解码为
%u2122
%AE

然后,我应该能够在匹配中相对轻松地使用
(char)(基-10整数值%uxxx或%XX)
将%u2122转换为
“\u2122”
,并使用正则表达式将%AE转换为
“\u00AE”


i、 e.-match
/%u([0-9a-f]{4})/i
,提取匹配的子表达式,将其转换为base-10,将其转换为字符并附加到输出,然后对
/%([0-9a-f]{2})/i执行相同的操作
您就快到了。EncodeURIComponent正确地编码为UTF-8,这是您今天应该在URL中始终使用的

问题是提交的查询字符串在进入服务器端脚本的过程中被破坏,因为getParameter()使用ISO-8559-1而不是UTF-8。这源于web使用UTF-8实现URI/IRI之前的远古时代,但令人遗憾的是,Servlet规范没有更新以符合现实,或者至少没有为其提供可靠的、受支持的选项

(Servlet 2.3中有request.setCharacterEncoding,但它不会影响查询字符串解析,而且如果以前读取过单个参数(可能是由其他框架元素读取的),那么它将根本不起作用。)


因此,您需要使用特定于容器的方法来获得适当的UTF-8,通常涉及server.xml中的内容。这对于在任何地方都可以运行的网络应用来说都是糟糕透顶的。对于Tomcat,请参见和。

我也遇到了同样的问题,通过使用urldecker()解码
Request.getQueryString()
,并在提取参数后解决了这个问题

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

有一种方法可以在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);
// ÏηγÏÏÏÏη // !!!!!!!!!!!!!!!!!!!!!!!!!!!
作品:

工作正常,但-改为尝试此操作(省略对decode()的调用,这不是必需的):


如前所述,如果
server.xml
被弄乱,如:

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1"
                     redirectPort="8443"  URIEncoding="UTF-8"/> 
对于多个连接器,仍然需要对此进行调整(查看注释掉的部分)。然后您将使用类似于:

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);
但是,如果
parameter=request.getParameter(“name”)使用字符集\u解码以进行\u URI\u编码已损坏,因此使用getBytes()获得的字节不是原始字节(这就是为什么默认情况下使用“iso-8859-1”)。您可以通过手动解析以下行中的查询字符串来消除这一切:

URLDecoder.decode(request.getQueryString().split("=")[1],
        CHARSET_FOR_URL_ENCODING);
我仍然在寻找文档中提到的
request.getParameter(“name”)
调用
urldecker.decode()
而不是返回
%CF%84%CE%B7%CE%B3%CF%81%CF%84%CF%83%CF%82%CE%B7
字符串的地方?如能在源代码中添加链接,将不胜感激。

另外,如何将字符串作为参数值传递,例如,
%CE
?=>请参阅注释:
参数=%25CE

Jetty的某些版本中存在一个错误,使其无法正确解析更高数量的UTF-8字符。如果您的服务器正确地接受阿拉伯语字母,但不接受表情符号,则表明您的版本存在此问题,因为阿拉伯语不在ISO-8859-1中,而是在UTF-8字符的较低范围内(“较低”表示java将以单个字符表示)

我从版本7.2.0.v20101020更新到版本7.5.4.v20111024,修复了问题;我现在可以使用getParameter(String)方法,而不必自己解析它


如果您真的很好奇,可以深入研究org.eclipse.jetty.util.Utf8StringBuilder.append(byte)的版本,看看当utf-8代码足够高时,它是否正确地向字符串添加了多个字符,或者如7.2.0所示,它只需将一个int转换为一个字符并添加后缀。

感谢我对tomcat、jetty中使用的默认字符集的编码和解码所了解的一切 我用谷歌番石榴来解决我的问题

        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"));
String str=urldecker.decode(request.getQueryString(),StandardCharsets.UTF_8.name());
最终地图=
new String(parameter.getBytes(CHARSET_FOR_URI_ENCODING), CHARSET_FOR_URL_ENCODING);
URLDecoder.decode(request.getQueryString().split("=")[1],
        CHARSET_FOR_URL_ENCODING);
        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"));