仅当 ContentType === JSON 时更改 Java 过滤器中的 ContentType 或 CharacterEncoding
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23084182/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Change ContentType or CharacterEncoding in Java Filter ONLY IF ContentType === JSON
提问by Brad Parks
I'm trying to ensure that all JSON responses from a Jersey based java application have a UTF-8 character encoding parameter appended to their ContentType header.
我试图确保来自基于 Jersey 的 Java 应用程序的所有 JSON 响应都将 UTF-8 字符编码参数附加到它们的 ContentType 标头。
So if it's a JSON response, I would like the response header for the Content-Type
to be
所以,如果它是一个JSON响应,我想响应报头的Content-Type
要
Content-Type: application/json;charset=UTF-8
内容类型:application/json;charset=UTF-8
EDIT: I know I can do this on a case by case basis, but I'd like to do it globally, so it affects all content responses that have a content type of "application/json".
EDIT: I know I can do this on a case by case basis, but I'd like to do it globally, so it affects all content responses that have a content type of "application/json".
If I just try and set the character encoding in my filter regardless of the content type, it works fine. But I only want to set the character encoding if the ContentType is "application/json". I find that the response.getContentType() method always returns null unless I call chain.doFilter first. But if I try and change the Character Encoding after this, it seems to always get overwritten.
如果我只是尝试在我的过滤器中设置字符编码而不管内容类型如何,它都可以正常工作。但如果 ContentType 是“application/json”,我只想设置字符编码。我发现 response.getContentType() 方法总是返回 null 除非我先调用 chain.doFilter 。但是如果我在此之后尝试更改字符编码,它似乎总是被覆盖。
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.ws.rs.core.MediaType;
public class EnsureJsonResponseIsUtf8Filter implements Filter
{
private class SimpleWrapper extends HttpServletResponseWrapper
{
public SimpleWrapper(HttpServletResponse response)
{
super(response);
}
@Override
public String getCharacterEncoding()
{
return "UTF-8";
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
chain.doFilter(request, response);
if (response.getContentType() != null && response.getContentType().contains(MediaType.APPLICATION_JSON))
{
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, new SimpleWrapper((HttpServletResponse) response));
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
@Override
public void destroy()
{
}
}
I've seen other similar questions, but none them seem to have this issue. I've tried registering my filter as the first, and last filter with no luck.
我见过其他类似的问题,但他们似乎没有这个问题。我尝试将我的过滤器注册为第一个和最后一个过滤器,但没有成功。
采纳答案by Brad Parks
Thanks to the other answers on this page, I found a way to do it.... Very close to what they were suggesting, but it turned out the only way I could get it to work was to override "getOutputStream" and look at the contentType at that point. I've put this filter as the first filter in the chain, and it seems to work fine.
感谢此页面上的其他答案,我找到了一种方法来做到这一点......非常接近他们的建议,但结果证明我可以让它工作的唯一方法是覆盖“getOutputStream”并查看那个时候的内容类型。我已将此过滤器作为链中的第一个过滤器,它似乎工作正常。
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.ws.rs.core.MediaType;
public class EnsureJsonIsUtf8ResponseFilter implements Filter
{
final String APPLICATION_JSON_WITH_UTF8_CHARSET = MediaType.APPLICATION_JSON + ";charset=" + java.nio.charset.StandardCharsets.UTF_8;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletResponse r = (HttpServletResponse) response;
HttpServletResponse wrappedResponse = new HttpServletResponseWrapper(r)
{
@Override
public ServletOutputStream getOutputStream() throws java.io.IOException
{
ServletResponse response = this.getResponse();
String ct = (response != null) ? response.getContentType() : null;
if (ct != null && ct.toLowerCase().startsWith(MediaType.APPLICATION_JSON))
{
response.setContentType(APPLICATION_JSON_WITH_UTF8_CHARSET);
}
return super.getOutputStream();
}
};
chain.doFilter(request, wrappedResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
// This method intentionally left blank
}
@Override
public void destroy()
{
// This method intentionally left blank
}
}
回答by Torindo
Not 100% sure I got what you're trying to achieve. Do you want to set the header charset after calling
不是 100% 确定我得到了你想要达到的目标。是否要在调用后设置标题字符集
chain.doFilter(request, response)
?
?
If that is the case I'm afraid you cannot because very likely at that point, after that chain.doFilter(request, response) has returned and the request processed, the content charset has already been sent to the client and therefore you cannot alter it anymore.
如果是这种情况,恐怕你不能,因为很可能在那个时候,在 chain.doFilter(request, response) 返回并处理请求之后,内容字符集已经发送到客户端,因此你不能改变它了。
回答by Cebence
Using this answeras reference the solution to your question is to re-encode the JSON text as shown here:
使用此答案作为参考,您的问题的解决方案是重新编码 JSON 文本,如下所示:
public void doFilter(...) {
final CharResponseWrapper wrappedResponse =
new CharResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, wrappedResponse);
final String content = wrappedResponse.toString();
final String type = wrappedResponse.getContentType();
if (type != null && type.contains(MediaType.APPLICATION_JSON)) {
// Re-encode the JSON response as UTF-8.
response.setCharacterEncoding("UTF-8");
final OutputStream out = response.getOutputStream();
out.write(content.getBytes("UTF-8"));
out.close();
}
else {
// Otherwise just write it as-is.
final PrintWriter out = response.getWriter();
out.write(content);
out.close();
}
}
回答by boky
This is not going to work in this way.
这不会以这种方式工作。
When you call chain.doFilter(request, response);
your headers are already flushed and you can't reset them later on.
当您调用chain.doFilter(request, response);
您的标头时,您的标头已被刷新,以后无法重置它们。
What you can do is actually a quick and dirty trick:
你可以做的实际上是一个快速而肮脏的技巧:
public void doFilter(...) {
HttpServletResponse resp = new HttpServletResponseWrapper(response) {
public void setContentType(String ct) {
if(ct!=null && ct.toLowerCase().startsWith("application/json")) {
super.setContentType("application/json;charset=UTF-8");
} else {
super.setContentType(ct);
}
}
}
// Set content type manually to override any potential defaults,
// See if you need it at all
response.setContentType("application/json;charset=UTF-8");
chain.doFilter(request, resp); // Inject our response!
}
EDIT:ct.toUpperCase().startsWith("application/json")
changed to ct.toLowerCase().startsWith("application/json")
.
编辑:ct.toUpperCase().startsWith("application/json")
更改为ct.toLowerCase().startsWith("application/json")
.
回答by Brad Parks
This also may be doable using a ClientFilter
, which I've just come across a StackOverflow post for a similar purpose:
这也可以使用 a ClientFilter
,我刚刚遇到了一个 StackOverflow 帖子,用于类似目的:
回答by Ben Romberg
Had success using a ContainerResponseFilter
:
成功使用了ContainerResponseFilter
:
public class ContentTypeEncodingFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
String contentType = responseContext.getHeaderString(HttpHeaders.CONTENT_TYPE);
if (contentType == null) {
return;
}
ContentType parsedType = ContentType.parse(contentType);
if (parsedType.getCharset() != null) {
return;
}
ContentType encodedType = parsedType.withCharset(StandardCharsets.UTF_8);
responseContext.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, encodedType.toString());
}
}