Spring @RestController响应Content-Type修改:Filter设置不生效问题
如何修复Filter中修改HttpServletResponse的Content-Type不生效问题
我尝试在Filter类中修改HttpServletResponse的Content-Type,但修改未生效。响应的Content-Type仍为application/json,预期应为application/xml。
现有代码
Filter类代码
@Component @Order(1) public class TestFilter implements javax.servlet.Filter { @Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { if (arg1 instanceof HttpServletResponse) { HttpServletResponse httpServletResponse = (HttpServletResponse) arg1; httpServletResponse.setContentType("application/xml"); arg1.setContentType("application/xml"); String contentType = httpServletResponse.getContentType(); System.out.println("contentType : "+contentType); arg2.doFilter(arg0, new HttpServletResponseWrapper(httpServletResponse) { @Override public void setContentType(final String type) { } }); String contentType1 = httpServletResponse.getContentType(); System.out.println("contentType1 : "+contentType1); String contentType2 = httpServletResponse.getContentType(); System.out.println("contentType2 : "+contentType2); } } }
Controller类代码
@RestController public class TestController { @PostMapping(path="/test", consumes = MediaType.ALL_VALUE, produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE }) public String test() { return "<SUCCESS></SUCCESS>"; } }
响应日志
contentType : application/xml contentType1 : application/json contentType2 : application/json
问题分析
- 现有
HttpServletResponseWrapper仅重写了setContentType但为空实现,Spring MVC内部可能直接操作原始响应对象的Header设置Content-Type,导致拦截失效。 - Controller的
produces声明了两种响应类型,Spring MVC会优先根据请求的Accept头选择;若请求未指定Accept,默认会选用数组第一个类型application/json,覆盖Filter中设置的值。
修复方案
方案一:增强Filter的拦截逻辑
修改Filter,同时拦截setContentType和setHeader方法,强制将Content-Type设置为目标值,并且在请求处理完成后再次确认设置:
@Component @Order(1) public class TestFilter implements javax.servlet.Filter { @Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { if (arg1 instanceof HttpServletResponse) { HttpServletResponse httpServletResponse = (HttpServletResponse) arg1; final String targetContentType = "application/xml"; // 初始化设置 httpServletResponse.setContentType(targetContentType); System.out.println("contentType : " + httpServletResponse.getContentType()); // 包装响应,拦截所有修改Content-Type的操作 HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(httpServletResponse) { @Override public void setContentType(String type) { // 强制使用目标类型 super.setContentType(targetContentType); } @Override public void setHeader(String name, String value) { if ("Content-Type".equalsIgnoreCase(name)) { super.setHeader(name, targetContentType); } else { super.setHeader(name, value); } } }; // 继续执行请求链 arg2.doFilter(arg0, responseWrapper); // 最后再确认设置一次,防止遗漏 httpServletResponse.setContentType(targetContentType); System.out.println("contentType after doFilter: " + httpServletResponse.getContentType()); } } }
方案二:直接修改Controller的produces
如果不需要动态切换响应类型,直接将Controller的produces指定为唯一的application/xml,Spring MVC会直接返回该类型:
@RestController public class TestController { @PostMapping(path="/test", consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_XML_VALUE) public String test() { return "<SUCCESS></SUCCESS>"; } }
注意事项
- 若请求的
Accept头明确指定了application/json,方案一的Filter会强制覆盖为application/xml,而方案二会直接返回application/xml,忽略Accept头。 - Filter的
@Order(1)确保它在其他Filter之前执行,避免被后续Filter覆盖设置。
内容的提问来源于stack exchange,提问作者ASP
相关产品推荐
相关产品推荐

