1. 概述
在本文中,我們將探討前端應用程序與部署在不同位置的 REST API 之間的通信。
目標是在解決瀏覽器中 CORS 和同一來源策略限制的情況下,允許 UI 調用 API,即使它們不共享同一來源。
我們將基本上創建兩個單獨的應用程序——一個 UI 應用程序和一個簡單的 REST API,並且我們將使用 Zuul 代理 在 UI 應用程序中作為代理來調用 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-netflix-zuul</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
最新版本可以在這裏找到 這裏。
3. Zuul 屬性
接下來,我們需要配置 Zuul,由於我們使用了 Spring Boot,我們將會在 application.yml
zuul:
routes:
foos:
path: /foos/**
url: http://localhost:8081/spring-zuul-foos-resource/foos
請注意:
- 我們正在將請求代理到我們的資源服務器 Foos
- 所有從 UI 發起的以 “/foos/” 開頭的請求都將路由到我們的 Foos 資源服務器,地址為 http://loclahost:8081/spring-zuul-foos-resource/foos/
4. API
我們的API應用程序是一個簡單的Spring Boot應用程序。在本文中,我們將考慮部署在運行在端口8081上的API。
首先,讓我們定義我們使用的資源的基準DTO:
public class Foo {
private long id;
private String name;
// standard getters and setters
}
以及一個簡單的控制器:
@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));
}
}
5. The UI Application
我們的UI應用程序也是一個簡單的Spring Boot應用程序。
在本文中,我們將考慮在運行在 8080端口上的API。
讓我們從主 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 使用相對 URL!
請記住,API應用程序未部署在與UI應用程序相同的服務器上,因此相對 URL 不應該工作,並且在沒有代理的情況下也不會工作。
有了代理,我們通過 Zuul 代理訪問 Foo 資源,該代理當然配置為將這些請求路由到 API 實際部署的任何位置。
最後,啓用 Boot 的應用程序:
@EnableZuulProxy
@SpringBootApplication
public class UiApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
}
除了簡單的 Boot 註解之外,請注意我們還使用了 Zuul 代理的樣式化啓用,這非常酷、乾淨且簡潔。
6. 測試路由
現在,讓我們測試我們的UI應用程序,方法如下:
@Test
public void whenSendRequestToFooResource_thenOK() {
Response response = RestAssured.get("http://localhost:8080/foos/1");
assertEquals(200, response.getStatusCode());}
7. 自定義 Zuul 過濾器
有多個 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 過濾器
最後,讓我們測試以確保自定義過濾器正常工作——首先,我們將修改我們的 FooController 在 Foos 資源服務器上:
@RestController
public class FooController {
@GetMapping("/foos/{id}")
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 請求。