在嵌入式 Jetty 中设置默认字符编码和内容类型
Setting default character encoding and content type in embedded Jetty
我正在为嵌入式 Jetty 制作 DSL,但在设置 characterEncoding
和 contentType
时遇到问题。我希望用户能够为这两个字段指定默认值,但是 Jetty 让生活变得艰难。
res.characterEncoding = null
给 res.characterEncoding
值 iso-8859-1
.
res.characterEncoding = ""
,给 res.characterEncoding
值 ""
,但 res.contentType
变成 application/json;charset=
res.characterEncoding = ""
THEN res.characterEncoding = null
与 res.characterEncoding = ""
的效果相同
基于这种奇怪的行为,我得到了一个荒谬的代码片段:
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
}
}
但这不是正确的做法。有什么想法吗?
您正在与 Servlet 规范的一个过于复杂的方面作斗争。
Opinion: The HttpServletResponse.setContentType(String)
method should never have existed, it should have just been .setMimeType(String)
and .setCharacterEncoding(Charset)
让我们从字符编码的重要性开始。
访问 HttpServletResponse.getWriter()
时,实现必须解析响应字符编码和区域设置以用于创建的 PrintWriter
。这意味着此时的字符编码将被赋值。
请注意,语言环境也被使用,这一点经常被忽略,但由于您是库编写者,所以应该向您指出这一点。参见 HttpServletResponse.setLocale(Locale)
和 HttpServletResponse.getLocale()
。
还有一点要考虑的是,如果您已经访问了 HttpServletResponse.getWriter()
,那么稍后使用 HttpServletResponse.setCharacterEncoding(String)
会导致 no-op,并且使用 HttpServletResponse.setContentType(String)
charset
会导致 charset
从生成的 header 中剥离。 (同样,这符合 Servlet 规范行为)。
HttpServletResponse.getCharacterEncoding()
可以 return 字符编码,如果手动设置为有效值,或者基于 Content-Type
如果已经声明,否则默认为 ISO-8859- 1.如果它使用 Content-Type
它首先检查 charset
参数并使用它。如果 Content-Type
没有 charset
,那么它将使用您的网络元数据中的 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
object.
当调用 HttpServletResponse.setCharacterEncoding(String)
时,它还负责修改响应中的 Content-Type
字段和 header。
假设尚未调用 .getWriter()
,使用 setCharacterEncoding(null)
将删除 Content-Type
字段和 header 上的任何现有 charset
参数。 setCharacterEncoding("utf-8")
会将 add/change charset
参数传递给 Content-Type
字段上的 utf-8
和 header。
调用 HttpServletResponse.setContentType(String)
时,如果提供了 charset
参数,它还负责修改字符编码字段。
了解所有这些后,您将意识到必须注意各种 API 调用的顺序,尤其是在进行 HttpServletResponse.getWriter()
调用时。
与其使用 Servlet HttpServletResponse APIs 来管理它,您或许应该在 Web 应用程序启动时通过 application/json
的 Web 元数据来控制它。
在您的情况下,您只需在 JettyServerUtil.kt
正在创建的 ServletContextHandler
上配置 MimeTypes
,不需要您正在使用的任何技巧。
Jetty 的 ServletContextHandler
和 MimeTypes
配置的默认行为不会添加字符集,因为 application/json
在 org/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-Type
mime-type,访问了PrintWriter
,设置了headerX-Charset
显示当前字符编码值,然后写入json内容。
客户端看到的Content-Type
响应header不包括utf-8
的假定charset
。
我正在为嵌入式 Jetty 制作 DSL,但在设置 characterEncoding
和 contentType
时遇到问题。我希望用户能够为这两个字段指定默认值,但是 Jetty 让生活变得艰难。
res.characterEncoding = null
给 res.characterEncoding
值 iso-8859-1
.
res.characterEncoding = ""
,给 res.characterEncoding
值 ""
,但 res.contentType
变成 application/json;charset=
res.characterEncoding = ""
THEN res.characterEncoding = null
与 res.characterEncoding = ""
基于这种奇怪的行为,我得到了一个荒谬的代码片段:
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
}
}
但这不是正确的做法。有什么想法吗?
您正在与 Servlet 规范的一个过于复杂的方面作斗争。
Opinion: The
HttpServletResponse.setContentType(String)
method should never have existed, it should have just been.setMimeType(String)
and.setCharacterEncoding(Charset)
让我们从字符编码的重要性开始。
访问 HttpServletResponse.getWriter()
时,实现必须解析响应字符编码和区域设置以用于创建的 PrintWriter
。这意味着此时的字符编码将被赋值。
请注意,语言环境也被使用,这一点经常被忽略,但由于您是库编写者,所以应该向您指出这一点。参见 HttpServletResponse.setLocale(Locale)
和 HttpServletResponse.getLocale()
。
还有一点要考虑的是,如果您已经访问了 HttpServletResponse.getWriter()
,那么稍后使用 HttpServletResponse.setCharacterEncoding(String)
会导致 no-op,并且使用 HttpServletResponse.setContentType(String)
charset
会导致 charset
从生成的 header 中剥离。 (同样,这符合 Servlet 规范行为)。
HttpServletResponse.getCharacterEncoding()
可以 return 字符编码,如果手动设置为有效值,或者基于 Content-Type
如果已经声明,否则默认为 ISO-8859- 1.如果它使用 Content-Type
它首先检查 charset
参数并使用它。如果 Content-Type
没有 charset
,那么它将使用您的网络元数据中的 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
object.
当调用 HttpServletResponse.setCharacterEncoding(String)
时,它还负责修改响应中的 Content-Type
字段和 header。
假设尚未调用 .getWriter()
,使用 setCharacterEncoding(null)
将删除 Content-Type
字段和 header 上的任何现有 charset
参数。 setCharacterEncoding("utf-8")
会将 add/change charset
参数传递给 Content-Type
字段上的 utf-8
和 header。
调用 HttpServletResponse.setContentType(String)
时,如果提供了 charset
参数,它还负责修改字符编码字段。
了解所有这些后,您将意识到必须注意各种 API 调用的顺序,尤其是在进行 HttpServletResponse.getWriter()
调用时。
与其使用 Servlet HttpServletResponse APIs 来管理它,您或许应该在 Web 应用程序启动时通过 application/json
的 Web 元数据来控制它。
在您的情况下,您只需在 JettyServerUtil.kt
正在创建的 ServletContextHandler
上配置 MimeTypes
,不需要您正在使用的任何技巧。
Jetty 的 ServletContextHandler
和 MimeTypes
配置的默认行为不会添加字符集,因为 application/json
在 org/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-Type
mime-type,访问了PrintWriter
,设置了headerX-Charset
显示当前字符编码值,然后写入json内容。
客户端看到的Content-Type
响应header不包括utf-8
的假定charset
。