【问题标题】:Setting default character encoding and content type in embedded Jetty在嵌入式 Jetty 中设置默认字符编码和内容类型
【发布时间】:2018-12-03 11:57:41
【问题描述】:

我正在为嵌入式 Jetty 制作 DSL,但在设置 characterEncodingcontentType 时遇到问题。我希望用户能够为这两个字段指定默认值,但 Jetty 让生活变得艰难。

res.characterEncoding = null 将值 res.characterEncoding 赋予 iso-8859-1

res.characterEncoding = "",赋予res.characterEncoding"",但res.contentType 变为application/json;charset=

res.characterEncoding = "" THEN res.characterEncoding = nullres.characterEncoding = "" 效果一样

基于这种奇怪的行为,我最终得到了一个荒谬的代码 sn-p:

if (res.characterEncoding.contains(";javalin-default") || res.contentType.contains(";javalin-default")) {
    res.contentType = res.contentType.replace(";javalin-default", "")
    res.characterEncoding = null
    if (res.contentType.contains("application/json")) {
        res.contentType = "application/json"
    } else {
        res.characterEncoding = defaultCharacterEncoding
    }
}

但这不是正确的做法。有什么想法吗?

我有一个问题:https://github.com/tipsy/javalin/issues/259

【问题讨论】:

    标签: java servlets kotlin jetty embedded-jetty


    【解决方案1】:

    您正在与 Servlet 规范的一个过于复杂的方面作斗争。

    意见:HttpServletResponse.setContentType(String) 方法不应该存在,它应该只是 .setMimeType(String).setCharacterEncoding(Charset)

    让我们从字符编码的重要性开始。

    当访问HttpServletResponse.getWriter() 时,实现必须解析响应字符编码和区域设置以用于创建的PrintWriter。这意味着此时的字符编码将被赋值。

    请注意,也使用了语言环境,这经常被忽略,但是由于您是库编写者,因此应该向您指出这一点。请参阅HttpServletResponse.setLocale(Locale)HttpServletResponse.getLocale()

    另外需要考虑的是,如果您已经访问过HttpServletResponse.getWriter(),那么稍后使用HttpServletResponse.setCharacterEncoding(String) 会导致无操作,而将HttpServletResponse.setContentType(String)charset 一起使用会导致charset被从产生的标题中剥离。 (同样,这符合 Servlet 规范行为)。

    如果之前手动设置为有效值,HttpServletResponse.getCharacterEncoding() 可以返回字符编码,或者如果已经声明,则基于Content-Type,否则默认为 ISO-8859-1。如果它使用Content-Type,它首先检查charset 参数并使用它。如果Content-Type 没有charset,那么它会在您的Web 元数据中使用mime-type 配置。此调用不应产生空字符编码。

    使用 HttpServletResponse.getCharacterEncoding() 的 Servlet 规范默认值为 ISO-8859-1(该值取自 RFC2616,当时定义了 Servlet 规范的这一方面)。

    Web 元数据来自 Web 描述符(又名 WEB-INF/web.xml)、默认描述符、覆盖描述符、org/eclipse/jetty/http/mime.properties 资源、org/eclipse/jetty/http/encoding.properties、其他存在的功能(例如 GzipHandler)和编程配置。

    在 Jetty 中,所有这些 mime-type 的各种配置源都会生成一个已配置的 Jetty org.eclipse.jetty.http.MimeTypes 对象。

    HttpServletResponse.setCharacterEncoding(String)被调用时,它也有责任修改响应上的Content-Type字段和头部。

    假设尚未调用 .getWriter(),使用 setCharacterEncoding(null) 将删除 Content-Type 字段和标头上的所有现有 charset 参数。 setCharacterEncoding("utf-8") 会将 charset 参数添加/更改为 utf-8Content-Type 字段和标题上。

    在调用HttpServletResponse.setContentType(String) 时,如果提供了charset 参数,它还负责修改字符编码字段。

    了解这一切后,您会意识到必须注意各种 API 调用的顺序,尤其是在进行 HttpServletResponse.getWriter() 调用时。

    与其使用 Servlet HttpServletResponse API 来管理它,不如在 webapp 启动时通过 application/json 的 web 元数据来控制它。

    在您的情况下,您只需在您的JettyServerUtil.kt 正在创建的ServletContextHandler 上配置MimeTypes,不需要您使用的任何黑客。

    Jetty 的ServletContextHandlerMimeTypes 配置的默认行为不会添加字符集,因为application/jsonorg/eclipse/jetty/http/encoding.properties 资源中是如何定义的(作为假定的字符集)。

    例子:

    package jetty;
    
    import static java.nio.charset.StandardCharsets.UTF_8;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.URI;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.server.handler.DefaultHandler;
    import org.eclipse.jetty.server.handler.HandlerList;
    import org.eclipse.jetty.servlet.DefaultServlet;
    import org.eclipse.jetty.servlet.ServletContextHandler;
    import org.eclipse.jetty.util.IO;
    
    public class MimeTypeJsonExample
    {
        public static void main(String[] args) throws Exception
        {
            Server server = new Server(9090);
            ServletContextHandler context = new ServletContextHandler();
            context.setContextPath("/");
            context.addServlet(JsonServlet.class, "/demo");
            context.addServlet(DefaultServlet.class, "/"); // handle static content and errors for this context
            HandlerList handlers = new HandlerList();
            handlers.addHandler(context);
            handlers.addHandler(new DefaultHandler()); // handle non-context errors
            server.setHandler(context);
            server.start();
    
            try
            {
                demonstrateJsonBehavior(server.getURI().resolve("/"));
            }
            finally
            {
                server.stop();
            }
        }
    
        private static void demonstrateJsonBehavior(URI serverBaseUri) throws IOException
        {
            HttpURLConnection http = (HttpURLConnection) serverBaseUri.resolve("/demo").toURL().openConnection();
            dumpRequestResponse(http);
            System.out.println();
            try (InputStream in = http.getInputStream())
            {
                System.out.println(IO.toString(in, UTF_8));
            }
        }
    
        private static void dumpRequestResponse(HttpURLConnection http) throws IOException
        {
            System.out.println();
            System.out.println("----");
            System.out.printf("%s %s HTTP/1.1%n", http.getRequestMethod(), http.getURL());
            System.out.println("----");
            System.out.printf("%s%n", http.getHeaderField(null));
            http.getHeaderFields().entrySet().stream()
                    .filter(entry -> entry.getKey() != null)
                    .forEach((entry) -> System.out.printf("%s: %s%n", entry.getKey(), http.getHeaderField(entry.getKey())));
        }
    
        public static class JsonServlet extends HttpServlet
        {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
            {
                resp.setContentType("application/json");
                PrintWriter writer = resp.getWriter();
                resp.setHeader("X-Charset", resp.getCharacterEncoding());
                writer.println("{\"mode\":[\"a=b\"],\"animals\":[[\"kiwi bird\",\"kea\",\"skink\"]]}");
            }
        }
    }
    

    这导致输出...

    2018-06-27 09:00:32.754:INFO::main: Logging initialized @360ms to org.eclipse.jetty.util.log.StdErrLog
    2018-06-27 09:00:32.898:INFO:oejs.Server:main: jetty-9.4.11.v20180605; built: 2018-06-05T18:24:03.829Z; git: d5fc0523cfa96bfebfbda19606cad384d772f04c; jvm 9.0.4+11
    2018-06-27 09:00:32.969:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@5dd6264{/,null,AVAILABLE}
    2018-06-27 09:00:33.150:INFO:oejs.AbstractConnector:main: Started ServerConnector@60707857{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
    2018-06-27 09:00:33.151:INFO:oejs.Server:main: Started @764ms
    
    ----
    GET http://192.168.0.119:9090/demo HTTP/1.1
    ----
    HTTP/1.1 200 OK
    Server: Jetty(9.4.11.v20180605)
    X-Charset: utf-8
    Content-Length: 58
    Date: Wed, 27 Jun 2018 14:00:33 GMT
    Content-Type: application/json
    
    {"mode":["a=b"],"animals":[["kiwi bird","kea","skink"]]}
    
    2018-06-27 09:00:33.276:INFO:oejs.AbstractConnector:main: Stopped ServerConnector@60707857{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
    2018-06-27 09:00:33.278:INFO:oejsh.ContextHandler:main: Stopped o.e.j.s.ServletContextHandler@5dd6264{/,null,UNAVAILABLE}
    

    如你所见,JsonServlet只设置Content-Typemime-type,访问PrintWriter,设置一个headerX-Charset显示当前字符编码值,然后写入json内容。

    客户端看到的Content-Type响应头不包括utf-8的假设charset

    【讨论】:

      猜你喜欢
      • 2015-04-24
      • 2017-03-24
      • 2022-11-03
      • 2016-03-13
      • 1970-01-01
      • 1970-01-01
      • 2014-05-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多