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, поэтому его легко импортировать и запускать как есть.