Change ContentType or CharacterEncoding in Java Filter ONLY IF ContentType === JSON

ε祈祈猫儿з 提交于 2019-11-29 21:45:10

问题


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.

So if it's a JSON response, I would like the response header for the Content-Type to be

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".

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.

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.


回答1:


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.

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



回答2:


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.

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").




回答3:


Using this answer as reference the solution to your question is to re-encode the JSON text as shown here:

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



回答4:


This also may be doable using a ClientFilter, which I've just come across a StackOverflow post for a similar purpose:

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




回答5:


Had success using a 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());
    }
}



回答6:


Not 100% sure I got what you're trying to achieve. Do you want to set the header charset after calling

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.



来源:https://stackoverflow.com/questions/23084182/change-contenttype-or-characterencoding-in-java-filter-only-if-contenttype-j

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!