帶有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的項目,因此應易於導入和運行。