如何使用 java 过滤器修改响应正文?

Posted

技术标签:

【中文标题】如何使用 java 过滤器修改响应正文?【英文标题】:how modify the response body with java filter? 【发布时间】:2019-01-20 10:17:10 【问题描述】:

我想对 HTTP 响应(json 格式)执行一些过滤逻辑。

我已成功更改响应正文,但是当正文的(字符串)大小发生更改时:我错过了最后一个字符。

为了更简单,我创建了一个简单的 Spring Boot 应用程序,我的 rest 控制器只有 Web 依赖项。

我的休息控制器

@RestController
@RequestMapping("/home/")
public class RestControllerHome 

@GetMapping (produces=MediaType.APPLICATION_JSON_VALUE)
public String home() 
        return " \"name\" : \"Peter\" ";
  

我的过滤器

@Component
public class MyFilter implements Filter 

@Override
public void destroy()  

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException 

    htmlResponseWrapper capturingResponseWrapper = new HtmlResponseWrapper((HttpServletResponse) response);
    filterChain.doFilter(request, capturingResponseWrapper);        
    if (response.getContentType() != null && response.getContentType().contains("application/json")) 
        String content = capturingResponseWrapper.getCaptureAsString();

        // This code works fine
        //response.getWriter().write(content.toUpperCase());

        // This code doesn't works because the content size is changed
        response.getWriter().write(" \"name\" : \"************r\" ");

    


@Override
public void init(FilterConfig arg0) throws ServletException       

HttpServletResponseWrapper // 在写入之前捕获响应

public class HtmlResponseWrapper extends HttpServletResponseWrapper 

private final ByteArrayOutputStream capture;
private ServletOutputStream output;
private PrintWriter writer;

public HtmlResponseWrapper(HttpServletResponse response) 
    super(response);
    capture = new ByteArrayOutputStream(response.getBufferSize());


@Override
public ServletOutputStream getOutputStream() 
    if (writer != null) 
        throw new IllegalStateException("getWriter() has already been called on this response.");
    

    if (output == null) 
        // inner class - lets the wrapper manipulate the response 
        output = new ServletOutputStream() 
            @Override
            public void write(int b) throws IOException 
                capture.write(b);
            

            @Override
            public void flush() throws IOException 
                capture.flush();
            

            @Override
            public void close() throws IOException 
                capture.close();
            

            @Override
            public boolean isReady() 
                return false;
            

            @Override
            public void setWriteListener(WriteListener arg0) 
            
        ;
    

    return output;


@Override
public PrintWriter getWriter() throws IOException 
    if (output != null) 
        throw new IllegalStateException("getOutputStream() has already been called on this response.");
    

    if (writer == null) 
        writer = new PrintWriter(new OutputStreamWriter(capture,
                getCharacterEncoding()));
    

    return writer;


@Override
public void flushBuffer() throws IOException 
    super.flushBuffer();

    if (writer != null) 
        writer.flush();
     else if (output != null) 
        output.flush();
    


public byte[] getCaptureAsBytes() throws IOException 
    if (writer != null) 
        writer.close();
     else if (output != null) 
        output.close();
    

    return capture.toByteArray();


public String getCaptureAsString() throws IOException 
    return new String(getCaptureAsBytes(), getCharacterEncoding());



在我的 doFilter 方法中,以下代码...

// This code works fine
response.getWriter().write(content.toUpperCase());

// This code doesn't works because the content size is changed
//response.getWriter().write(" \"name\" : \"************r\" ");

... 给我以下输出: "NAME": "彼得" 这告诉我,代码工作正常。

但是,实际上我想更改正文内容...

// This code works fine
//response.getWriter().write(content.toUpperCase());

// This code doesn't works because the content size is changed
response.getWriter().write(" \"name\" : \"************r\" ");

... 和前面的代码,给了我一个不完整的文本体作为输出: ** "名称" : "**********

我做错了什么? 我的应用程序有一个更大的 json 主体,过滤器中的逻辑更复杂一些。但是,如果我不让这个工作,我就无法让我的其余代码工作。请帮忙。

我从https://www.leveluplunch.com/java/tutorials/034-modify-html-response-using-filter/获取了Filter和HttpServletResponseWrapper

【问题讨论】:

您可能需要将 Content-Length 标头设置为正确的值。 JBNizet,非常感谢。您很快解决了我的问题,再次感谢您。 【参考方案1】:

感谢 JBNizet 的帮助,我发现解决方案是添加 Content Lenght:

String newContent = " \"name\" : \"************r\" ";
response.setContentLength(newContent .length());
response.getWriter().write(newContent);

【讨论】:

我收到getWriter() has already been called for this response 这个解决方案的错误。能够与medium.com/@sportans300/… 合作吗?【参考方案2】:

其实按照上面的回答,你会得到一个错误提示getWriter() has already been called for this response

所以,你可以像下面这样简单地修改最后一行:

String newContent = " \"name\" : \"************r\" ";
response.setContentLength(newContent.length());
response.getOutputStream().write(newContent.getBytes());

【讨论】:

以上是关于如何使用 java 过滤器修改响应正文?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Zuul 后置过滤器中拦截和编辑响应正文?

如何在 Zuul 后置过滤器中获取响应正文?

如何在 Asp.Net Web API 2 中使用 Owin OAuth2 修改令牌端点响应正文

如何在提交之前修改 Spring Cloud Gateway 中的响应正文

WebClient - 如何获取请求正文?

当 HTTP 请求返回状态 401 时,如何在 Java 中解析响应正文