Modifying the Response Body in a Zuul Filter – 在Zuul过滤器中修改响应体

最后修改: 2020年 2月 21日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’re going to look at Netflix Zuul’s post filter.

在本教程中,我们要看一下Netflix Zuul的帖子过滤器。

Netflix Zuul is an edge service provider that sits between an API client and a plethora of microservices.

Netflix Zuul是一个边缘服务提供商,位于API客户端和大量微服务之间。

The post-filter runs before the final responses are sent to the API client. This gives us the opportunity to act on the raw response body and do things like logging and other data transformations we desire.

后置过滤器在最终响应被发送到API客户端之前运行。这使我们有机会对原始响应体采取行动,并做一些我们希望的事情,如记录和其他数据转换。

2. Dependencies

2.依赖性

We’re going to be working with Zuul in a Spring Cloud environment. So let’s add the following to the dependency management section of our pom.xml:

我们将在Spring Cloud环境中与Zuul一起工作。因此,让我们在pom.xml的依赖性管理部分添加以下内容:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2020.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        <version>2.2.2.RELEASE</version>
    </dependency>
</dependencies>

The latest version of the Spring Cloud dependencies and spring-cloud-starter-netflix-zuul can be found on Maven Central.

最新版本的Spring Cloud依赖项spring-cloud-starter-netflix-zuul可以在Maven Central找到。

3. Creating a Post Filter

3.创建一个帖子过滤器

A post filter is a regular class that extends the abstract class ZuulFilter and has a filter type of post:

帖子过滤器是一个扩展了抽象类ZuulFilter的普通类,其过滤器类型为post

public class ResponseLogFilter extends ZuulFilter {
    
    @Override
    public String filterType() {
        return POST_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        return null;
    }
}

Please note that we returned POST_TYPE in the filterType() method. This is what actually differentiates this filter from other types.

请注意,我们在filterType()方法中返回POST_TYPE。这就是这个过滤器与其他类型过滤器的实际区别所在。

Another important method to take note of is the shouldFilter() method. We’re returning true here since we want the filter to be run in the filter chain.

另一个需要注意的重要方法是shouldFilter()方法。我们在这里返回true,因为我们希望过滤器能在过滤器链中运行。

In a production-ready application, we may externalize this configuration for better flexibility.

在一个生产就绪的应用中,我们可以将这种配置外部化,以获得更好的灵活性。

Let’s take a closer look at the run() which gets called whenever our filter is running.

让我们仔细看看run(),它在我们的过滤器运行时被调用。

4. Modifying the Response Body

4.修改响应主体

As previously stated, Zuul sits between microservices and their clients. Consequently, it can access the response body and optionally modify it before passing it down.

如前所述,Zuul位于微服务和其客户端之间。因此,它可以访问响应体,并在向下传递之前有选择地修改它。

For example, we can read the response body and log its content:

例如,我们可以读取响应体并记录其内容。

@Override
public Object run() throws ZuulException {

    RequestContext context = RequestContext.getCurrentContext();
    try (final InputStream responseDataStream = context.getResponseDataStream()) {

        if(responseDataStream == null) {
            logger.info("BODY: {}", "");
            return null;
        }

        String responseData = CharStreams.toString(new InputStreamReader(responseDataStream, "UTF-8"));
        logger.info("BODY: {}", responseData);

        context.setResponseBody(responseData);
    }
    catch (Exception e) {
        throw new ZuulException(e, INTERNAL_SERVER_ERROR.value(), e.getMessage());
    }

    return null;
}

The snippet above shows the full implementation of the run() method in the ResponseLogFilter we created earlier. First, we obtained an instance of the RequestContext. And from that context, we were able to get the response data InputStream in a try with resources construct.

上面的片段显示了我们之前创建的ResponseLogFilterrun()方法的完整实现。首先,我们获得了一个RequestContext的实例。从该上下文中,我们能够在一个有资源的尝试结构中获得响应数据InputStream

Note that the response input stream can be null, which is why we check for it. This can be due to service timeout or other unexpected exceptions on the microservice. In our case, we just log an empty response body when this occurs.

请注意,响应输入流可以是空的,这就是我们检查它的原因。这可能是由于服务超时或微服务上的其他意外异常。在我们的案例中,当这种情况发生时,我们只是记录一个空的响应体。

Going forward, we read the input stream into a String that we can then log.

往前走,我们把输入流读成一个String,然后我们可以记录。

Very importantly, we add the response body back to the context for processing using the context.setResponseBody(responseData). If we omit this step, we’ll get an IOException along the following lines: java.io.IOException: Attempted read on a closed stream.

非常重要的是,我们使用context.setResponseBody(responseData)将响应体添加回上下文进行处理。如果我们省略了这一步,我们将得到一个IOException,其内容如下。java.io.IOException。试图在一个关闭的流上进行读取

5. Conclusion

5.结论

In conclusion, post filters in Zuul offer an opportunity for developers to do something with the service response before sending it to the client.

总之,Zuul中的后置过滤器为开发者提供了一个机会,在将服务响应发送到客户端之前对其进行处理。

However, we have to be cautious not to expose sensitive information accidentally which can lead to a breach.

然而,我们必须谨慎,不要意外地暴露敏感信息,这可能导致漏洞。

Moreover, we should be wary of doing long-running tasks within our post filter as it can add considerably to the response time.

此外,我们应该警惕在我们的后置过滤器中做长期运行的任务,因为这可能会大大增加响应时间。

As usual, the source code is available over on GitHub.

像往常一样,源代码可在GitHub上获得。