DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Spring Boot: Cross-Origin AJAX HTTP Requests
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • How To Build Web Service Using Spring Boot 2.x
  • How To Validate HTTP Post Request Body - Restful Web Services With Spring Framework | Spring Boot

Trending

  • Build Your First AI Model in Python: A Beginner's Guide (1 of 3)
  • Java 23 Features: A Deep Dive Into the Newest Enhancements
  • How to Build Local LLM RAG Apps With Ollama, DeepSeek-R1, and SingleStore
  • A Guide to Container Runtimes
  1. DZone
  2. Coding
  3. Frameworks
  4. Extending Spring Boot's Zuul Proxy to Record Requests and Responses

Extending Spring Boot's Zuul Proxy to Record Requests and Responses

A code snippet that allows the user to extend Spring Boot's Zuul proxy to record requests and responses.

By 
Dan Erez user avatar
Dan Erez
·
Aug. 06, 19 · Code Snippet
Likes (14)
Comment
Save
Tweet
Share
23.9K Views

Join the DZone community and get the full member experience.

Join For Free

An easy way to capture the incoming requests to your server and replay them can be very useful. For example, if you lack an automation team, or simply want to be quick about it, you can record the REST requests of a scenario and play them again after every push to GIT. You can also use the recorded requests to simulate many users. Or, you can analyze the requests to learn useful statistics, etc. You get the idea.

OK, so how can we do it the easy way? First, we’ll exploit spring boot Zuul gateway filters feature – this gateway can activate custom filters for every request and every response. A handler must simply extend ZuulFilter and provide information of:

  • The Filter’s type, for example, ‘pre’ means a pre-routing filter. Other values can be ‘post’ for post-routing filters or ‘route’ for routing to an origin.
  • The Filter’s order in the chain of filters. A lower number will cause the filter to be executed before a filter with a higher number.
  • Should filter — if the filter should be applied for a given route.
  • Run — the actual things you want to do.

We’ll use these filters to record the incoming requests (listing 1) and outgoing responses (listing 2) requests to a file. Naturally, you can record them to DB, to S3, or to whatever repository you’ll be comfortable to use later. No, you can analyze this information at your leisure. The ‘global Id’ is a GUID saved on ThreadLocal and helps us to bind together matching requests and responses.

@Component

@Profile("recording")

public class LogRequestFilter extends ZuulFilter {

    privatestatic Logger log = LoggerFactory.getLogger(LogRequestFilter.class);

    @Value("${recording.file:c:/temp/record.txt}")

    private String recordFile;

    @Override

    public String filterType() {

        return "pre";

    }

    @Override

    publicint filterOrder() {

        return 2;

    }
    @Override

    publicboolean shouldFilter() {

        return true;

    }
    @Override

    public Object run() {

        RequestContext ctx = RequestContext.getCurrentContext();

        HttpServletRequest request = new HttpServletRequestWrapper(ctx.getRequest());

        String requestData = null;

        try {

            if (request.getContentLength() > 0) {

                requestData = CharStreams.toString(request.getReader());

            }

        } catch (Exception e) {

            log.error("Error parsing request", e);

            throw e;

        }

        try {

            String line = String.format("Request, %s, %s,%s,%s \r\n",

                getContext().getGlobalId(), request.getRequestURL(),

                request.getMethod(), requestData);

            BufferedWriter bw = Files.newBufferedWriter(Paths.get(recordFile),

                Charset.forName("UTF-8"), StandardOpenOption.APPEND);

            bw.write(line);

            bw.close();

        } catch (IOException e) {

            log.error("Error writing request", e);

            throw e;

        }

        returnnull;

    }

}
Listing 2– Recording outgoing responses

@Component

@Profile("recording")

publicclass LogResponseFilter extends ZuulFilter {

    privatestatic Logger logger = LoggerFactory.getLogger(LogResponseFilter.class);

    @Value("${recording.file:c:/temp/record.txt}")

    private String recordFile;

    @Override

    public String filterType() {

        return "post";

    }

    @Override

    publicint filterOrder() {

        return 2;

    }

    @Override

    publicboolean shouldFilter() {

        return true;

    }

    @Override

    public Object run() {

        RequestContext ctx = RequestContext.getCurrentContext();

        try (final InputStream responseDataStream = ctx.getResponseDataStream())

        {

            final String responseData =

                CharStreams.toString(new InputStreamReader(responseDataStream,

                    "UTF-8"));

            try {

                String line = String.format("Response, %s, %s \r\n",

                    getContext().getGlobalId(), responseData);

                BufferedWriter bw = Files.newBufferedWriter(Paths.get(recordFile),

                    Charset.forName("UTF-8"), StandardOpenOption.APPEND);

                bw.write(line);

                bw.close();

            } catch (IOException e) {

                logger.error("Error writing response", e);

                throw e;

            }

            ctx.setResponseBody(responseData);

        } catch (IOException e) {

            logger.error("Error reading body", e);

            throw e;

        }

        returnnull;

    }

}


Note that both beans are active only when a ‘recording’ profile is declared in application.properties .

A common use case would be to use this data for ‘automation’, meaning running the recorded scenarios whenever needed (after a code push, nightly, etc.). Request values can be replaced by parameters, that will be populated from excel (for example) and enable running the same test with different values.

Replaying can be done straightforwardly, using Spring’s RestTemplate. The recording file is the input to the player, which simply sends the requests lines as REST requests, using the recorded data as a request body for POST requests, where needed.

We can also add a validator that compares the responses to the response lines in the recording. Since some values in the response will be different for sure (such as timestamp fields, or id fields for created objects) we can make the validator smarter and do validations like ‘id must not be null’ instead of a naïve string comparison.

Sending multiple requests in parallel to check load balancing (or simply see the behavior of your app with more than one request at a time) is fairly simple as well, using Java’s Executor to run the same recording multiple times in parallel.

These were just a few examples of usages for such recordings. If you find some other useful usages, please let me know!

Requests Spring Framework Spring Boot Record (computer science) Filter (software) zuul

Opinions expressed by DZone contributors are their own.

Related

  • Spring Boot: Cross-Origin AJAX HTTP Requests
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • How To Build Web Service Using Spring Boot 2.x
  • How To Validate HTTP Post Request Body - Restful Web Services With Spring Framework | Spring Boot

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!