Перейти к основному содержимому

Spring REST с прокси-сервером Zuul

· 4 мин. чтения

1. Обзор

В этой статье мы рассмотрим взаимодействие между интерфейсным приложением и REST API, развернутыми отдельно .

Цель состоит в том, чтобы обойти ограничения CORS и политики одинакового происхождения для браузера и позволить пользовательскому интерфейсу вызывать API, даже если они не имеют одного и того же источника.

В основном мы создадим два отдельных приложения — приложение пользовательского интерфейса и простой REST API, и мы будем использовать прокси- сервер Zuul в приложении пользовательского интерфейса для проксирования вызовов к REST API.

Zuul — это маршрутизатор на основе JVM и балансировщик нагрузки на стороне сервера от Netflix. И Spring Cloud имеет хорошую интеграцию со встроенным прокси-сервером Zuul — это то, что мы будем использовать здесь.

2. Конфигурация Maven

Во-первых, нам нужно добавить зависимость поддержки zuul из Spring Cloud в pom.xml нашего приложения пользовательского интерфейса :

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>

Последнюю версию можно найти здесь .

3. Зуул Свойства

Далее нам нужно настроить 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/.

4. API

Наше приложение API — это простое приложение Spring Boot.

В рамках этой статьи мы рассмотрим API, развернутый на сервере, работающем на порту 8081.

Давайте сначала определим базовый 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. Пользовательский интерфейс приложения

Наше приложение с пользовательским интерфейсом также является простым приложением Spring Boot.

В рамках этой статьи мы рассмотрим API, развернутый на сервере, работающем на порту 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>

Наиболее важным аспектом здесь является то, как мы обращаемся к API, используя относительные URL-адреса!

Имейте в виду, что приложение API не развертывается на том же сервере, что и приложение пользовательского интерфейса, поэтому относительные URL-адреса не должны работать и не будут работать без прокси-сервера.

Однако с прокси-сервером мы получаем доступ к ресурсам Foo через прокси-сервер Zuul, который, конечно же, настроен на маршрутизацию этих запросов туда, где на самом деле развернут API.

И, наконец, собственно загрузочное приложение:

@EnableZuulProxy
@SpringBootApplication
public class UiApplication extends SpringBootServletInitializer {

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

Обратите внимание, что помимо простой аннотации Boot, мы также используем стиль аннотации enable-style для прокси-сервера 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;
}
// ...
}

Этот простой фильтр просто добавляет заголовок « Тест » к запросу, но, конечно, мы можем сделать его настолько сложным, насколько нам нужно, чтобы расширить наши запросы.

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-запрос в пути.

Полную реализацию этого руководства можно найти в проекте GitHub — это проект на основе Maven, поэтому его легко импортировать и запускать как есть.