【问题标题】:Cannot write new response in servlet filter无法在 servlet 过滤器中写入新响应
【发布时间】:2019-05-20 21:08:21
【问题描述】:

我正在尝试读取当前响应并尝试用新数据覆盖响应。但目前我无法这样做。我总是以 GetWriter 已被调用的异常结束

这就是我想要做的事情

我想从响应中读取、修改它并在过滤器中写入一个新响应。我无法正常执行此操作,因为该过程由 swagger-springfox 处理。我正在寻找这种方法的原因是有一个 xml 元素 - 它在 APIDOC 中生成以被删除。我知道这是一次性的,可能存在一些配置问题,但是用于扫描和确定问题所在的文件太多。因此,我决定采用这种方法。此链接中提供了更多详细信息 - https://github.com/springfox/springfox/issues/2821

在我打印的字符串中 - 它工作得非常好,但是响应永远不会发送到客户端,因为似乎响应的 getWriter() 要么已经被调用,要么正在关闭。

这是一个示例 JSON 字符串,您可以 - String s = " <Json>{ \"id\": 1, \"itemName\": \"theItem \", \"owner\": { \"id\": 2, \"name\": \"theUser\" }} </Json>";

这是我的过滤器

public class SwaggerFilter extends GenericFilterBean {


 @Override
  public void doFilter(ServletRequest request, ServletResponse response, 
   FilterChain chain)
          throws IOException, ServletException {
      // TODO Auto-generated method stub
      HttpServletRequest req = (HttpServletRequest) request;
      HttpServletResponse res = (HttpServletResponse) response;

    if (req.getRequestURI().toLowerCase().endsWith("someURL")) {

        try {

            ResponseWrapper wrapperResponse = new ResponseWrapper(res);
            chain.doFilter(req, wrapperResponse);

            String responseContent = new String(wrapperResponse.getDataStream());
            System.out.println("response obtained:" + responseContent);

            try {

                List<String> s13 = Stream.of(responseContent).filter((s1) -> s1.contains("<Json>"))
                        .map((sample) -> Arrays.asList(sample.split(" ")))
                        .flatMap((listString) -> {
                            StringBuffer sb = new StringBuffer();
                            listString.forEach(item -> {

                                sb.append(item);

                            });
                            return Stream.of(sb.toString().trim().replace("<Json>", "").replace("</Json>", ""));
                        }).collect(Collectors.toList());
                s13.forEach(item -> System.out.println("items :" + item));
                String s14 = String.join("", s13);
                System.out.println("tt" + s14);
                PrintWriter writer = wrapperResponse.getWriter();
                writer.write(s14);
                writer.close();
            } catch (Exception e) {
                System.out.println(e.getLocalizedMessage());

            }

        } finally {
        }
    }
    chain.doFilter(req, res);
}

}

这是servlet输出流

public class FilterServletOutputStream extends ServletOutputStream {

ByteArrayOutputStream bos;

public FilterServletOutputStream(OutputStream output) {
    bos = (ByteArrayOutputStream) output;
}

@Override
public boolean isReady() {
    // TODO Auto-generated method stub
    return false;
}

@Override
public void setWriteListener(WriteListener listener) {
    // TODO Auto-generated method stub

}

@Override
public void write(int b) throws IOException {
    // TODO Auto-generated method stub
    bos.write(b);

}

@Override
public void write(byte[] arg0, int arg1, int arg2) throws IOException {
    bos.write(arg0, arg1, arg2);
}

@Override
public void write(byte[] arg0) throws IOException {
    bos.write(arg0);
}

}

这是响应包装器

public class ResponseWrapper extends HttpServletResponseWrapper {

ByteArrayOutputStream output;
FilterServletOutputStream filterOutput;

public ResponseWrapper(HttpServletResponse response) {
    super(response);
    output = new ByteArrayOutputStream();
    // TODO Auto-generated constructor stub
}

@Override
public ServletOutputStream getOutputStream() {
    if (filterOutput == null) {
        filterOutput = new FilterServletOutputStream(output);
    }
    return filterOutput;
}

public byte[] getDataStream() {
    return output.toByteArray();
}

}

错误:

Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'server.servlet.contextPath' in value "${server.servlet.contextPath}"
    at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:172) ~[spring-core-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124) ~[spring-core-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:237) ~[spring-core-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:211) ~[spring-core-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:834) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1086) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:91) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:373) ~[spring-beans-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    ... 22 common frames omitted

您能否确定问题出在哪里并帮助编写新的响应数据并将其发送过来?

更新: 我已经重写了大部分。这是代码。不知何故,响应文档是空的。我觉得我几乎可以破解它。有人可以在最后帮助我吗?

@Component
@Order(2)
public class DumpFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
            throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;

        HttpServletResponse response = (HttpServletResponse) servletResponse;
        ByteArrayPrinter pw = new ByteArrayPrinter();
        HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response) {
            @Override
            public PrintWriter getWriter() {
                return pw.getWriter();
            }

            @Override
            public ServletOutputStream getOutputStream() {
                return pw.getStream();
            }

        };
        System.out.println("before chaingin");
        chain.doFilter(httpRequest, wrappedResp);

        byte[] bytes = pw.toByteArray();
        String respBody = new String(bytes);
        if (respBody.startsWith("<Json>")) {
            System.out.println("in if");

            List<String> s13 = Stream.of(respBody).filter((s1) -> s1.contains("<Json>"))
                    .map((sample) -> Arrays.asList(sample.split(" ")))
                    .flatMap((listString) -> {
                        StringBuffer sb = new StringBuffer();
                        listString.forEach(item -> {

                            sb.append(item);

                        });
                        return Stream.of(sb.toString().trim().replace("<Json>", "").replace("</Json>", ""));
                    }).collect(Collectors.toList());
            s13.forEach(item -> {
                System.out.println("items in list:" + item);
                try {
                    response.getOutputStream().write(item.getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }

            });

            // String s14 = String.join("", s13);
            // System.out.println("s14" + s14.getBytes());
            // bytes = s14.getBytes();
            // response.getOutputStream().write(s14.getBytes());
        } else {
            response.getOutputStream().write(bytes);
        }

        System.out.println("RESPONSE -> " + new String(bytes));

    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }
}

这里是使用的辅助类:

public class ByteArrayPrinter {

    private ByteArrayOutputStream baos = new ByteArrayOutputStream();

    private PrintWriter pw = new PrintWriter(baos);

    private ServletOutputStream sos = new ByteArrayServletStream(baos);

    public PrintWriter getWriter() {
        return pw;
    }

    public ServletOutputStream getStream() {
        return sos;
    }

    byte[] toByteArray() {
        return baos.toByteArray();
    }
}




public class ByteArrayServletStream extends ServletOutputStream {

    ByteArrayOutputStream baos;

    ByteArrayServletStream(ByteArrayOutputStream baos) {
        this.baos = baos;
    }

    @Override
    public void write(int param) throws IOException {
        baos.write(param);
    }

    @Override
    public boolean isReady() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public void setWriteListener(WriteListener listener) {
        // TODO Auto-generated method stub

    }

}

在调用 v2/api-docs?groupName=XXXXXX 时,我遇到了错误 - 第 1 行第 1 列的错误:文档为空

【问题讨论】:

    标签: spring-boot servlet-filters


    【解决方案1】:

    我终于找到了答案。对于那些偶然发现此类问题的人,这里是它的答案

    问题#1 如果我们让这个过滤器作为第一个过滤器执行,那么问题是通过以最高优先级对其进行排序,这将使过滤器链接和链接并且将永久运行。所以放弃尝试订购过滤器。

    问题2#

    如果您尝试执行过滤器,由于响应已损坏,默认情况下会将 content-type 标头设置为 application/xhtml 或 text/html(感谢我的同事指出),并且浏览器会尝试使用相同的内容类型执行。但是在 POSTMAN 和 SOAPUI 中。它会完美地执行它

    问题#3 由于我们包装了一个响应,我们不能直接设置内容类型。所以当我们得到 outputStream 时,它必须在包装器中设置

    这里是实现:

    @Component
    public class SwaggerFilter implements Filter {
    
        final String APPLICATION_XHTML = "application/xhtml";
        final String XML_ELEMENT_START = "<Json>";
        final String XML_ELEMENT_END = "</Json>";
    
        @Override
        public void init(FilterConfig config) throws ServletException {
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
                throws IOException, ServletException {
            // TODO Auto-generated method stub
            HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
    
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            ByteArrayPrinter pw = new ByteArrayPrinter();
            HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response) {
    
                @Override
                public void setContentType(final String type) {
                    super.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                }
    
                @Override
                public PrintWriter getWriter() {
                    return pw.getWriter();
                }
    
                @Override
                public ServletOutputStream getOutputStream() throws IOException {
                    ServletResponse response = this.getResponse();
    
                    String ct = (response != null) ? response.getContentType() : null;
                    if (ct != null && ct.contains(APPLICATION_XHTML)) {
                        response.setContentType(ct + "," + MediaType.APPLICATION_JSON_UTF8_VALUE);
                    }
                    return pw.getStream();
                }
    
            };
            chain.doFilter(httpRequest, wrappedResp);
    
            byte[] bytes = pw.toByteArray();
            String respBody = new String(bytes);
            if (respBody.startsWith(XML_ELEMENT_START)) {
    
                List<String> s13 = Stream.of(respBody).filter((s1) -> s1.contains(XML_ELEMENT_START))
                        .map((sample) -> Arrays.asList(sample.split(" ")))
                        .flatMap((listString) -> {
                            StringBuffer sb = new StringBuffer();
                            listString.forEach(item -> {
    
                                sb.append(item);
    
                            });
                            return Stream
                                    .of(sb.toString().trim().replace(XML_ELEMENT_START, "").replace(XML_ELEMENT_END, ""));
                        }).collect(Collectors.toList());
    
                String s14 = String.join("", s13);
    
                response.getOutputStream().write(s14.getBytes());
            } else {
                response.getOutputStream().write(bytes);
            }
    
        }
    
        @Override
        public void destroy() {
            // TODO Auto-generated method stub
    
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      对于我在上面提到的 (@Joey587) 的情况,它是从邮递员那里工作的,并且从浏览器端得到错误。因此,如果您遇到 sam 问题,请尝试从邮递员端发送访问页面的请求。

      干杯

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-04-17
        • 2014-12-14
        • 2023-03-05
        • 2015-11-11
        • 1970-01-01
        • 2014-10-13
        • 2013-03-16
        • 1970-01-01
        相关资源
        最近更新 更多