无法在 servlet 过滤器中写入新响应 [英] Cannot write new response in servlet filter

查看:48
本文介绍了无法在 servlet 过滤器中写入新响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to read the current response and trying to overwrite the response with new data. But currently I am unable to do so. I always end up with the exception that the GetWriter has already been called

这是我想要做的

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

I would like to read from the response, modify it and write a new response in the filter. I would not be able to do it normally since the process is handled by swagger-springfox. The reason I am looking for this approach is to have an xml element - which gets generated in the APIDOC to be removed. I understand that this is a one off and probably have some configuration issues but there are one too many files for scanning and identifying where the issue is. Hence I decided to go with this approach. More details are available in this link - https://github.com/springfox/springfox/issues/2821

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

In the string that I printing - it works perfectly fine, but the response is never getting sent to the client because it seems that the getWriter() of the response is either already been called or getting closed.

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

Here is a sample JSON string that you could - 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);
}

}

这是servletoutputStream

This is the servletoutputStream

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

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

Could you please identify where the issue is and help in writing the new response data and send it over?

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

UPDATE: I have rewritten most of it. here is the code. Somehow the response document is coming as empty. I feel I have almost able to crack it. Could someone please help me out at the end part?

@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 列错误:文档为空

On calling the v2/api-docs?groupName=XXXXXX, I am ending up with the error - error on line 1 at column 1: Document is empty

推荐答案

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

Atlast I have found the answer. For those who stumble upon such issue here is the answer to it

问题#1如果我们让这个过滤器作为第一个过滤器执行它的问题是通过以最高优先级排序它会使过滤器链接和链接并永久运行.所以放弃尝试订购过滤器.

Issue#1 The problem if we make this filter execute it as the first filter by ordering it with HIGHEST PRECEDENCE will make the filter chaining and chaining and will run for perpetual time. So discard trying to order the filter.

问题 2#

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

If you try to execute the filter , since the response is corrupted, the content-type header will be set to application/xhtml or text/html (thanks to my colleague who pointed that out) by default and the browser tries to execute with the same content-type. But in POSTMAN and SOAPUI. It will execute it flawlessly

问题#3由于我们包装了一个 response ,我们不能直接设置 content-type .所以当我们得到 outputStream 时它必须在包装器中设置

Issue#3 Since we are wrapping a response , we cannot set the content-type directly. So it has to set in the wrapper when we get the 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

    }

}

这篇关于无法在 servlet 过滤器中写入新响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆