Spring REST with Zuul Proxy: A Step-by-Step Tutorial

This tutorial demonstrates how to use Spring Cloud's Zuul proxy to route requests from a separate UI application to a REST API, covering Maven setup, Zuul configuration, API and UI code, custom filters, and testing, while addressing CORS and same‑origin restrictions.

Architecture Digest
Architecture Digest
Architecture Digest
Spring REST with Zuul Proxy: A Step-by-Step Tutorial

1. Overview

This article explores communication between a frontend UI application and a separately deployed REST API, aiming to solve CORS and same‑origin policy limitations so that the UI can call the API even when they are not on the same origin.

Two independent applications are created—a UI app and a simple REST API—using a Zuul proxy in the UI to forward API calls.

Zuul is a JVM‑based router and load balancer from Netflix that integrates smoothly with Spring Cloud.

2. Maven Configuration

Add Spring Cloud Zuul support to the UI project's pom.xml:

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

3. Zuul Properties

Configure Zuul routes in application.yml (Spring Boot):

zuul:
  routes:
    foos:
      path: /foos/**
      url: http://localhost:8081/spring-zuul-foos-resource/foos

All UI requests starting with /foos/ will be routed to the Foos resource server.

4. API

The API is a simple Spring Boot application running on port 8081.

Define a DTO for the resource:

public class Foo {
  private long id;
  private String name;
  // standard getters and setters
}

And a basic controller:

@Controller
public class FooController {

  @RequestMapping(method = RequestMethod.GET, value = "/foos/{id}")
  @ResponseBody
  public Foo findById(@PathVariable long id, HttpServletRequest req, HttpServletResponse res) {
    return new Foo(Long.parseLong(randomNumeric(2)), randomAlphabetic(4));
  }
}

5. UI Application

The UI is also a Spring Boot app, deployed on port 8080, and starts with a simple index.html that uses AngularJS:

<html>
<body ng-app="myApp" ng-controller="mainCtrl">
<script src="angular.min.js"></script>
<script src="angular-resource.min.js"></script>

<script>
var app = angular.module('myApp', ["ngResource"]);

app.controller('mainCtrl', function($scope,$resource,$http) {
  $scope.foo = {id:0 , name:"sample foo"};
  $scope.foos = $resource("/foos/:fooId",{fooId:'@id'});

  $scope.getFoo = function(){
    $scope.foo = $scope.foos.get({fooId:$scope.foo.id});
  }
});
</script>

<div>
<h1>Foo Details</h1>
<span>{{foo.id}}</span>
<span>{{foo.name}}</span>
<a href="#" ng-click="getFoo()">New Foo</a>
</div>
</body>
</html>

The crucial point is that the UI must use relative URLs to reach the API; without a proxy, the calls would fail because the API is on a different server.

With Zuul enabled, the UI can access the Foo resource through the proxy, which forwards the request to the actual API location.

Enable the proxy with annotations:

@EnableZuulProxy
@SpringBootApplication
public class UiApplication extends SpringBootServletInitializer {

  public static void main(String[] args) {
    SpringApplication.run(UiApplication.class, args);
  }
}

6. Test Routing

Verify that the UI can successfully call the API through Zuul:

@Test
public void whenSendRequestToFooResource_thenOK() {
  Response response = RestAssured.get("http://localhost:8080/foos/1");
  assertEquals(200, response.getStatusCode());
}

7. Custom Zuul Filter

Create a custom filter to manipulate requests, for example adding a header named Test:

@Component
public class CustomZuulFilter extends ZuulFilter {

  @Override
  public Object run() {
    RequestContext ctx = RequestContext.getCurrentContext();
    ctx.addZuulRequestHeader("Test", "TestSample");
    return null;
  }

  @Override
  public boolean shouldFilter() {
    return true;
  }
  // ... other methods
}

8. Test Custom Zuul Filter

Modify the FooController to echo the header and test that the filter works:

@Controller
public class FooController {

  @GetMapping("/foos/{id}")
  @ResponseBody
  public Foo findById(@PathVariable long id, HttpServletRequest req, HttpServletResponse res) {
    if (req.getHeader("Test") != null) {
      res.addHeader("Test", req.getHeader("Test"));
    }
    return new Foo(Long.parseLong(randomNumeric(2)), randomAlphabetic(4));
  }
}
@Test
public void whenSendRequest_thenHeaderAdded() {
  Response response = RestAssured.get("http://localhost:8080/foos/1");
  assertEquals(200, response.getStatusCode());
  assertEquals("TestSample", response.getHeader("Test"));
}

9. Conclusion

The tutorial shows how to use Zuul to route UI requests to a REST API, solving CORS and same‑origin issues, and demonstrates how to customize and extend HTTP requests with custom filters. The full Maven project is available on GitHub.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaProxyCORSREST APISpring CloudZuul
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.