Servlet 过滤器“代理"仅作用于来自远程端点的响应 [英] Servlet filter "proxy" that only acts on response from remote endpoint

查看:39
本文介绍了Servlet 过滤器“代理"仅作用于来自远程端点的响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要某些 HTTP 请求必须重定向到 Spring Boot Web 应用程序/服务,但在请求端,Spring 应用程序什么都不做,而是充当 HTTP 客户端(另一个服务)和请求的真实目的地.但是当响应返回到 Spring 应用程序(从那个目的地)时,我需要 Spring 应用程序能够检查响应并在需要时可能对其采取行动.所以:

I have a need where certain HTTP requests must be redirected to a Spring Boot web app/service, but that on the request-side, the Spring app does nothing and acts as a passthrough between the HTTP client (another service) and the request's true destination. But when the response comes back to the Spring app (from that destination), I need the Spring app to be able to inspect the response and possibly take action on it if need be. So:

  1. HTTP 客户端向 http://someapi.example.com
  2. 发出请求
  3. 网络魔术将请求路由到我的 Spring 应用程序,例如 http://myproxy.example.com
  4. 根据请求,此应用/代理不执行任何操作,因此请求在 http://someapi.example.com 上转发
  5. http://someapi.example.com 处的服务端点将 HTTP 响应返回给代理
  6. 位于 http://myproxy.example.com 的代理会检查此响应,并可能在将响应返回给原始客户
  1. HTTP client makes a request to, say, http://someapi.example.com
  2. Network magic routes the request to my Spring app at, say, http://myproxy.example.com
  3. On the request, this app/proxy does nothing, and so the request is forwarded on http://someapi.example.com
  4. The service endpoint at http://someapi.example.com returns an HTTP response back to the proxy
  5. The proxy at http://myproxy.example.com inspects this response, and possibly sends an alert before returning the response back to the original client

本质上,过滤器充当请求的传递,并且仅在远程服务执行并返回响应之后真正执行任何操作.

So essentially, a filter that acts as a pass-through on the request, and only really does anything after the remote service has executed and returned a response.

到目前为止,我最好的尝试是设置一个 servlet 过滤器:

My best attempt thus far has been to setup a servlet filter:

@Override
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    chain.doFilter(request, response)

    // How and where do I put my code?
    if(responseContainsFizz(response)) {
        // Send an alert (don't worry about this code)
    }
}

这可以吗?如果是这样,我应该把检查和响应响应的代码放在哪里?使用我的代码,当我尝试从浏览器点击控制器时抛出异常:

Is this possible to do? If so, where do I put the code that inspects and acts upon the response? With my code the way it is I get exceptions thrown when trying to hit a controller from a browser:

java.lang.IllegalStateException: STREAM
    at org.eclipse.jetty.server.Response.getWriter(Response.java:910) ~[jetty-server-9.2.16.v20160414.jar:9.2.16.v20160414]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_92]
  rest of stack trace omitted for brevity

有什么想法吗?

推荐答案

根据 Servlet API 文档,您收到 IllegalStateException 的原因是因为您试图调用 ServletResponse.getWriterServletResponse.getOutputStream 之后已经在响应上被调用.所以看起来你需要调用的方法是ServletResponse.getOutputStream().

Per the Servlet API documentation, the reason you are getting the IllegalStateException is because you are attempting to call ServletResponse.getWriter after ServletResponse.getOutputStream has already been called on the response. So it appears that the method you need to call is ServletResponse.getOutputStream().

但是,如果您尝试访问响应的正文,最好的解决方案是将响应包装在 ServletResponseWrapper 中,以便您可以捕获数据:

However, if you are trying to access the body of the response, the best solution is to wrap the response in a ServletResponseWrapper so that you can capture the data:

public class MyFilter implements Filter
{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException
    {

    }

    @Override
    public void destroy()
    {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
    {
        MyServletResponseWrapper responseWrapper = new MyServletResponseWrapper((HttpServletResponse) response);
        chain.doFilter(request, responseWrapper);
        if (evaluateResponse(responseWrapper)) {
            // Send an alert
        }
    }

    private boolean evaluateResponse(MyServletResponseWrapper responseWrapper) throws IOException
    {
        String body = responseWrapper.getResponseBodyAsText();

        // Perform business logic on the body text

        return true;
    }

    private static class MyServletResponseWrapper extends HttpServletResponseWrapper
    {
        private ByteArrayOutputStream copyOutputStream;
        private ServletOutputStream wrappedOutputStream;

        public MyServletResponseWrapper(HttpServletResponse response)
        {
            super(response);
        }

        public String getResponseBodyAsText() throws IOException
        {
            String encoding = getResponse().getCharacterEncoding();
            return copyOutputStream.toString(encoding);
        }


        @Override
        public ServletOutputStream getOutputStream() throws IOException
        {
            if (wrappedOutputStream == null) {
                wrappedOutputStream = getResponse().getOutputStream();
                copyOutputStream = new ByteArrayOutputStream();
            }
            return new ServletOutputStream()
            {
                @Override
                public boolean isReady()
                {
                    return wrappedOutputStream.isReady();
                }

                @Override
                public void setWriteListener(WriteListener listener)
                {
                    wrappedOutputStream.setWriteListener(listener);
                }

                @Override
                public void write(int b) throws IOException
                {
                    wrappedOutputStream.write(b);
                    copyOutputStream.write(b);
                }

                @Override
                public void close() throws IOException
                {
                    wrappedOutputStream.close();
                    copyOutputStream.close();
                }
            };
        }
    }
}

这篇关于Servlet 过滤器“代理"仅作用于来自远程端点的响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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