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

假设我有:

<a href="http://www.yahoo.com/" target="_yahoo" title="Yahoo!™" 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")); 

我在浏览器中看到雅虎! 如果手动将浏览器字符编码切换为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"); 和/或其他将导致雅虎! TM出现在SERVLET输出中?

你快到了 EncodeURIComponent正确编码为UTF-8,这是你现在应该总是在URL中使用的。

问题是提交的查询string在进入服务器端脚本的过程中被破坏,因为getParameter()使用ISO-8559-1而不是UTF-8。 这是源于古代的时代,在UTF-8用于URI / IRI之前,但Servlet规范没有更新以符合现实,或者至less为它提供了可靠的支持选项,这是相当可悲的。

(在Servlet 2.3中有request.setCharacterEncoding,但是不影响查询stringparsing,如果之前已经读取了一个参数,可能还有一些其他的框架元素,它根本不能工作。

所以你需要用特定于容器的方法来获得正确的UTF-8,通常涉及到server.xml中的东西。 这完全吸引分布的Web应用程序,应该在任何地方工作。 对于Tomcat,请参阅http://wiki.apache.org/tomcat/FAQ/CharacterEncoding以及Tomcat 的“URIEncoding”,Encoding Filter和request.setCharacterEncoding之间的区别 。

我得到了同样的问题,并通过使用URLDecoder()解码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); // ÏηγÏÏÏÏη // !!!!!!!!!!!!!!!!!!!!!!!!!!! 

作品

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

工作,但会打破如果默认编码!= utf-8 – 试试这个,而不是(不需要调用decode()它不需要):

 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") )。 因此,对于防弹解决scheme,您必须获得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); 

如果parameter = request.getParameter("name");则这可能会失败( IIUC ) 用CHARSET_FOR_URI_ENCODING解码被破坏,所以我用getBytes()得到的字节不是原来的(这就是为什么默认使用“iso-8859-1” – 它将保留字节 )。 您可以通过手动parsing以下行中的查询string来摆脱这一切:

 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串? 源中的链接将非常感激。
另外我怎样才能通过参数的值string,比如%CE =>查看评论: parameter=%25CE

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

request.getCharacterEncoding()返回什么?

我真的不知道JavaScript如何处理编码或如何使用特定的编码。

您需要确保在所有阶段都正确使用编码 – 不要试图通过使用new String()getBytes()来修正数据,而不是在已经被错误编码的地方使用getBytes()

编辑:这可能有助于使原始页面(使用Javascript的)也以UTF-8编码,并在其内容types中声明。 那么我相信Javascript可能会默认使用UTF-8来处理它的请求 – 但这不是明确的知识,只是猜测而已。

你总是可以使用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> 

我想我可以得到以下工作:

 encodeURIComponent(escape(lnk.title)) 

这给了我%25u2122 (for&#8482)或%25AE (for&#174),它们将在servlet中分别解码为%u2122%AE

然后,我可以使用正则expression式中的(char) (base-10 integer value of %uXXXX or %XX)比较容易地将%u2122变成'\u2122'和%AE成'\u00AE'

即 – match /%u([0-9a-f]{4})/i ,提取匹配的子expression式,将其转换为base-10,将其转换为char并将其附加到输出,然后执行相同/%([0-9a-f]{2})/i

Jetty的某些版本中存在一个错误,它会错误地parsing更多数字的UTF-8字符。 如果你的服务器正确地接受了阿拉伯文字母而不是表情符号,这是一个你有这个问题的版本的标志,因为阿拉伯文不在ISO-8859-1中,而是在UTF-8字符的较低范围内(“lower”表示java将代表它在一个字符)。

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

如果你真的好奇,你可以挖掘你的org.eclipse.jetty.util.Utf8StringBuilder.append(byte)的版本,看看它是否正确地添加多个字符到utf-8代码足够高或者如果,如7.2.0所示,它只是将一个int转换为char并追加。