帶有Zuul代理的Spring REST
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-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.
- 用戶界面中所有以“
/foos/
”開頭的請求都將被路由到我們的Foos
資源服務器,http://loclahost:8081/spring-zuul-foos-resource/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. UI應用程序
我們的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 !
請記住,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過濾器
最後,讓我們測試一下以確保我們的自定義過濾器可以正常工作–首先,我們將在Foos資源服務器上修改FooController
:
@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請求。
可以在GitHub項目中找到本教程的完整實現–這是一個基於Maven的項目,因此應易於導入和運行。