仅在Java Filter中更改ContentType或CharacterEncoding如果ContentType === JSON

时间:2014-04-15 12:47:12

标签: java filter content-type jersey-2.0

我试图确保来自基于Jersey的java应用程序的所有JSON响应都在其ContentType标头中附加了UTF-8字符编码参数。

因此,如果它是JSON响应,我希望Content-Type的响应标头为

  

Content-Type: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".

如果我只是尝试在我的过滤器中设置字符编码,无论内容类型如何,它都可以正常工作。但是我只想设置字符编码,如果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()
    {
    }
}

我见过其他similar questions,但他们似乎都没有这个问题。我已经尝试将我的过滤器注册为第一个,最后一个过滤器没有运气。

6 个答案:

答案 0 :(得分:7)

感谢此页面上的其他答案,我找到了一种方法来做到这一点......非常接近他们的建议,但事实证明我能让它工作的唯一方法是覆盖&#34 ;的getOutputStream"并查看该点的contentType。我把这个过滤器作为链中的第一个过滤器,似乎工作正常。

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
    }
}

答案 1 :(得分:4)

这不会以这种方式起作用。

当您致电chain.doFilter(request, response);时,您的标题已被刷新,之后您无法重置它们。

你能做的实际上是一个快速而又肮脏的伎俩:

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!
}

编辑: ct.toUpperCase().startsWith("application/json")已更改为ct.toLowerCase().startsWith("application/json")

答案 2 :(得分:1)

使用this answer作为参考,您的问题的解决方案是重新编码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();
    }
}

答案 3 :(得分:1)

使用ClientFilter也可以这样做,我刚刚遇到类似目的的StackOverflow帖子:

https://stackoverflow.com/a/7464585/26510

答案 4 :(得分:1)

使用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());
    }
}

答案 5 :(得分:0)

不是100%肯定我得到了你想要达到的目标。 是否要在调用

后设置标题字符集
chain.doFilter(request, response)

如果是这种情况我害怕你不能,因为那时很可能,在那个chain.doFilter(请求,响应)已经返回并且处理了请求之后,内容字符集已经被发送到客户端了,因此你不能再改变它了。