http://www.baeldung.com/spring-rest-with-zuul-proxy
作者: Eugen Paraschiv
譯者: http://oopsguy.com
1、概述
在本文中,我們將探討前端應用與單獨部署的 REST API 之間的通信。
本文旨在解決 CORS 和瀏覽器的同源策略限制,允許 UI 調用 API,即使它們不是同源。
基本上,我們將創建兩個獨立的應用程序 — 一個 UI 應用程序和一個簡單的 REST API,我們將使用 UI 應用程序中的 Zuul 代理來代理對 REST API 的調用。
Zuul 是 Netflix 的一個基於 JVM 的路由和服務端負載均衡器。Spring Cloud 與內嵌式 Zuul 代理可以很好地集成工作 — 本次我們也將使用他們。
2、Maven 配置
首先,我們需要添加一個來自 Spring Cloud 的 zuul 支持到我們的 UI 應用程序的 pom.xml 中:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.0.4.RELEASE</version>
</dependency>
3、Zuul Properties
接下來,我們需要配置 Zuul,由於我們使用 Spring Boot,我們將在 application.yml 中進行配置:
zuul:
routes:
foos:
path: /foos/**
url: http://localhost:8081/spring-zuul-foos-resource/foos
注意:
- 我們正在代理我們的資源服務器 Foos。
- 以 /foos/ 開頭的 UI 的所有請求都將路由到我們的 Foos 資源服務器:http:// loclahost:8081/spring-zuul-foos-resource/foos/
4、API
我們的 API 應用程序是一個簡單的 Spring Boot 應用程序。
在本文中,我們考慮將 API 部署至運行在 8081 端口上的服務器中。
首先定義我們要使用的資源的 DTO:
public class Foo {
private long id;
private String name;
// standard getters and setters
}
和一個簡單的控制器:
@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 應用程序
我們的 UI 應用程序也是一個簡單的 Spring Boot 應用程序。
在本文中,我們考慮將 UI 部署至運行在 8080 端口上的服務器中。
我們從 index.html 開始 — 使用了一點 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>
這里最重要的地方是我們如何使用相對 URL 來訪問 API!
請記住,API 應用程序未部署在與 UI 應用程序相同的服務器上,因此無法通過相對 URL 工作,如果沒有使用代理,該 UI 應用程序將無法正常工作。
然而,如果使用代理服務器,我們可以通過 Zuul 代理來訪問 Foo 資源,該代理配置為將這些請求路由到實際部署 API 的位置。
最后,引導應用程序:
@EnableZuulProxy
@SpringBootApplication
public class UiApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
}
除了簡單的引導注解,請注意,我們使用 Zuul 代理的注解啟用方式,這非常酷,而且干凈簡潔。
6、測試路由
現在,讓我們來測試 UI 應用程序,如下所示:
@Test
public void whenSendRequestToFooResource_thenOK() {
Response response = RestAssured.get("http://localhost:8080/foos/1");
assertEquals(200, response.getStatusCode());
}
7、自定義 Zuul Filter
有多個 Zuul 過濾器可以使用,我們也可以創建自己自定義的過濾器:
@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;
}
// ...
}
這個簡單的過濾器只是在請求中添加了一個名為 Test 的頭部 — 當然,我們可以根據我們需要的復雜程度來擴充我們的請求。
8、測試自定義 Zuul Filter
最后,讓我們測試以確保我們自定義的過濾器能夠正常工作 — 首先我們將在 Foos 資源服務器上修改 FooController:
@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、結論
在這篇文章中,我們主要使用了 Zuul 將來自 UI 應用程序的請求路由到 REST API。我們成功地解決了 CORS 和同源策略,我們還定制和擴充了 HTTP 請求。
本教程的完整實現可以在項目 GitHub 中找到 — 這是一個基於 Maven 的項目,所以應該很容易導入和運行。
