2

我们正在构建的产品的一个要求是它的 URL 端点在用户的母语中具有语义意义。这意味着我们需要 UTF-8 编码的 URL 来支持所有的字母表。

我们也不希望必须为我们支持的每个应用程序服务器和版本提供安装配置文档,所以如果我们能在代码中完成这个就很好了。这可能是不可能的,因为当 Servlet 收到请求时,它已经被 App 服务器编码,等等。

通过重构请求的路径信息,我已经得到了这个工作(对于我第一个使用 ISO-Latin 非美国 ASCII 字符的用例):

String pathInfoEncoded = new String(httpServletRequest.getPathInfo().getBytes(), "UTF-8");

然后解析它。

但是,在使用 sendRedirect() 从 POST 重定向到 GET 后,这不起作用。请求的路径已经转义(所以 ö 被编码为 %F6)并且我上面的方法不起作用。

所以我想我的问题是我做错了吗?如果是这样,我的无知的解药是什么?:)

更新:找到解决方案。问题是 Servlet API 在发送重定向之前在 URL 编码方面有一些奇怪的行为。在调用 sendRedirect() 之前,您必须进行 URL 编码(转义 UTF-8 字符)。encodeRedirectURL() 方法不适合你。

本页讨论它: http: //www.whirlycott.com/phil/2005/05/11/building-j2ee-web-applications-with-utf-8-support/

4

3 回答 3

4

需要调查和试验的几件事:

  • 查看您的 ./conf/server.xml 文件并确保连接器的 URIEncoding 属性设置为“UTF-8”。

例如:

<Connector port="8080" 
           protocol="HTTP/1.1" 
           URIEncoding="UTF-8"/>
  • 使用某种基于浏览器的工具(例如:TamperData for FireFox)来查看您的浏览器正在向服务器发送什么——它很可能会为您转义它。如果是这种情况,您可以在服务器上使用URL.decode()它。
  • 手动设置标头和响应代码,而不是使用 Response.redirect()。

例如:

response.setHeader("Location", myUtf8unencodedUrl);
response.setStatus(response.SC_MOVED_TEMPORARILY);

没有承诺,但如果是我,这就是我会尝试的。:)

于 2009-05-11T08:12:36.510 回答
2

找到了解决方案。问题是 Servlet API 在发送重定向之前在 URL 编码方面有一些奇怪的行为。在调用 sendRedirect() 之前,您必须进行 URL 编码(转义 UTF-8 字符)。encodeRedirectURL() 方法不适合你。

本页讨论它: http: //www.whirlycott.com/phil/2005/05/11/building-j2ee-web-applications-with-utf-8-support/

于 2009-05-11T23:46:30.507 回答
1

我们在这里遇到了同样的情况,即我们的产品也需要以地球上可能的每种语言向用户显示有意义的 URL。我们所有的工具和技术都支持 UTF-8,所以这没有问题。转义 UTF-8 字符在技术上是可行的,但 IE (7, 8) 显示难看的转义 URL,而 Firefox 取消转义并显示漂亮的 url,即 '/français/Banane.html' 将在 IE 中显示为 '/fran% C3%A7ais/Banane.html'。POST 后的 GET / 表单提交后的重定向根本不起作用,既不发送 UTF-8 url,也不转义 UTF-8 url。我们还尝试使用 XML 样式的数字实体编码,但没有成功。

然而,我们终于找到了一种在 POST 之后成功重定向的方法:使用 ISO-8859-1 对 UTF-8 字符串进行字节编码。无论如何,我们都没有真正理解它是如何工作的(浏览器如何知道如何解码,因为每个 utf-8 字符的字节数可能会有所不同,浏览器如何知道它最初是 utf-8?),但是确实如此。

这是一个简单的 servlet 来尝试:


package springapp.web.servlet;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;

public class TestServlet extends HttpServlet {

 private static final long serialVersionUID = -1743198460341004958L;

 /* (non-Javadoc)
  * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
  */
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {

  String url = "çöffte.html"; 
  try {
         ServletContext context = req.getSession().getServletContext();
   // read utf8 encoded russian url
            if (context.getResource("/WEB-INF/ru_url.txt") != null){
             InputStream is = context.getResourceAsStream("/WEB-INF/ru_url.txt"); 
             if (is != null){
              url = IOUtils.toString(is, "UTF-8");
              System.out.println(String.format("Redirecting to [%s]", url));
             }
            }
        }
        catch (FileNotFoundException fNFEx) {
         fNFEx.printStackTrace();
        }
        catch (IOException ioEx) {
         ioEx.printStackTrace();
        }

        byte[] utfBytes = url.getBytes("UTF-8");
        String result = new String(utfBytes, "ISO-8859-1");
        resp.sendRedirect(result);

        // does not work:
        //resp.sendRedirect(url);
        //resp.sendRedirect(Utf8UrlEscaper.escapeUtf8(url));
        //resp.sendRedirect(Utf8UrlEscaper.escapeToNumericEntity(url));
 }
}

对于重定向目标,复制并粘贴任何本地语言 url,例如来自 wikipedia 的 utf-8 编码(没有 BOM!)文件,并将其保存在 WEB-INF 目录中。在我们的示例中,我们采用了一个俄语 url ( http://ru.wikipedia.org/wiki/Заглавная_страница ) 并将其保存在一个名为 'ru_url.txt' 的文件中。

我们创建了一个简单的 SpringMVC 应用程序,将任何 *.abc url 映射到测试 servlet。现在,如果您启动应用程序并输入“localhost:8080/springmvctest/a.abc”之类的内容,您应该被重定向到俄罗斯维基百科站点,并且浏览器(IE 和 Firefox、Safari 或其他可能不会)应该显示一个不错的 utf- 8 位编码的原生俄罗斯网址。

于 2009-09-17T13:39:31.530 回答