springcloud之Zuul

springcloud之Zuul

communication between a front-end application and a REST API that are deployed separately.

The goal is to work around CORS and the Same Origin Policy restriction of the browser and allow the UI to call the API even though they don't share the same origin.

Zuul is a JVM based router and server side load balancer by Netflix.


Dependency: spring-cloud-starter-netflix-zuul

Config properties:

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

API

port 8081

@RestController
public class FooController {

@GetMapping("/foos/{id}")
public Foo findById(
@PathVariable long id, HttpServletRequest req, HttpServletResponse res) {
return new Foo(Long.parseLong(randomNumeric(2)), randomAlphabetic(4));
}
}

UI


<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>

Ajax call : /foos/:fooId

UI and API deployed separately.

Zuul Proxy

@EnableZuulProxy
@SpringBootApplication
public class UiApplication extends SpringBootServletInitializer {

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

Zuul Filter

Eg post filter

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;
}
}

// modify response
@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;
}

Zuul Rate limit

@Controller
@RequestMapping("/greeting")
public class GreetingController {

@GetMapping("/simple")
public ResponseEntity<String> getSimple() {
return ResponseEntity.ok("Hi!");
}

@GetMapping("/advanced")
public ResponseEntity<String> getAdvanced() {
return ResponseEntity.ok("Hello, how you doing?");
}
}
zuul:
routes:
serviceSimple:
path: /greeting/simple
url: forward:/
serviceAdvanced:
path: /greeting/advanced
url: forward:/
ratelimit:
enabled: true
repository: JPA
policy-list:
serviceSimple:
- limit: 5
refresh-interval: 60
type:
- origin
serviceAdvanced:
- limit: 1
refresh-interval: 2
type:
- origin
strip-prefix: true

Notes: Zuul.ratelimit.policy-list

Rate limit of 5 requests per 60 seconds for the serviceSimple endpoint and ate limit of 1 request per 2 seconds

Type:

  • origin – rate limit based on the user origin request

  • url – rate limit based on the request path of the downstream service

  • user – rate limit based on the authenticated username or ‘anonymous'

    Fra


https://www.baeldung.com/spring-rest-with-zuul-proxy