Рубрики
Без рубрики

Введение в функциональный веб-фреймворк весной 5

Краткое и практическое руководство по новому функциональному веб-фреймворку весной 5

Автор оригинала: baeldung.

1. введение

Spring Web Flux-это новый функциональный веб-фреймворк, построенный с использованием реактивных принципов.

В этом уроке мы узнаем, как работать с ним на практике.

Мы будем основываться на нашем существующем руководстве по Spring 5 WebFlux . В этом руководстве мы создали простое реактивное приложение REST с использованием компонентов на основе аннотаций. Здесь вместо этого мы будем использовать функциональную структуру.

2. Зависимость Maven

Нам понадобится та же зависимость spring-boot-starter-web flux , что и в предыдущей статье:


    org.springframework.boot
    spring-boot-starter-webflux
    2.2.6.RELEASE

3. Функциональный Веб-Фреймворк

Функциональная веб-платформа вводит новую модель программирования, в которой мы используем функции для маршрутизации и обработки запросов.

В отличие от модели, основанной на аннотациях, где мы используем сопоставления аннотаций, здесь мы будем использовать функцию обработчика и функцию маршрутизатора s .

Аналогично, как и в аннотированных контроллерах, подход к функциональным конечным точкам построен на том же реактивном стеке.

3.1. Функция управления

Функция Обработчик представляет функцию, которая генерирует ответы на запросы, направляемые им:

@FunctionalInterface
public interface HandlerFunction {
    Mono handle(ServerRequest request);
}

Этот интерфейс в первую очередь представляет собой функцию <Запрос, ответ> , которая ведет себя очень похоже на сервлет.

Хотя, по сравнению со стандартной службой Servlet#(ServletRequest req, ServletResponse res) , функция обработчика не принимает ответ в качестве входного параметра.

3.2. Функция маршрутизации

Функция маршрутизатора служит альтернативой аннотации @RequestMapping . Мы можем использовать его для маршрутизации запросов к функциям обработчика:

@FunctionalInterface
public interface RouterFunction {
    Mono> route(ServerRequest request);
    // ...
}

Как правило, мы можем импортировать вспомогательную функцию Router Functions.router() для создания маршрутов вместо написания полной функции маршрутизатора.

Это позволяет нам направлять запросы, применяя предикат Request. Когда предикат сопоставлен, возвращается второй аргумент – функция обработчика:

public static  RouterFunction route(
  RequestPredicate predicate,
  HandlerFunction handlerFunction)

Поскольку метод route() возвращает функцию маршрутизатора , мы можем связать его в цепочку для построения мощных и сложных схем маршрутизации.

4. Реактивное Приложение REST С Использованием Функционального веб-Сайта

В нашем предыдущем руководстве мы создали простое приложение EmployeeManagement REST с использованием @RestController и WebClient.

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

Во-первых, нам нужно создать маршруты с помощью функции маршрутизатора для публикации и использования наших реактивных потоков Сотрудников s .

Маршруты регистрируются как компоненты Spring и могут быть созданы в любом классе конфигурации.

4.1. Единый ресурс

Давайте создадим наш первый маршрут с помощью функции маршрутизатора , которая публикует один Сотрудник ресурс:

@Bean
RouterFunction getEmployeeByIdRoute() {
  return route(GET("/employees/{id}"), 
    req -> ok().body(
      employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class));
}

Первый аргумент-это предикат запроса. Обратите внимание, как мы использовали статически импортированные предикаты запроса .Метод GET здесь. Второй параметр определяет функцию обработчика, которая будет использоваться, если применяется предикат.

Другими словами, приведенный выше пример направляет все запросы GET для /employees/{id} в EmployeeRepository#findEmployeeById(String id) метод.

4.2. Ресурс сбора

Далее, для публикации ресурса коллекции, давайте добавим еще один маршрут:

@Bean
RouterFunction getAllEmployeesRoute() {
  return route(GET("/employees"), 
    req -> ok().body(
      employeeRepository().findAllEmployees(), Employee.class));
}

4.3. Обновление Единого Ресурса

Наконец, давайте добавим маршрут для обновления ресурса Employee :

@Bean
RouterFunction updateEmployeeRoute() {
  return route(POST("/employees/update"), 
    req -> req.body(toMono(Employee.class))
      .doOnNext(employeeRepository()::updateEmployee)
      .then(ok().build()));
}

5. Составление Маршрутов

Мы также можем составить маршруты вместе в одной функции маршрутизатора.

Давайте посмотрим, как объединить маршруты, созданные выше:

@Bean
RouterFunction composedRoutes() {
  return 
    route(GET("/employees"), 
      req -> ok().body(
        employeeRepository().findAllEmployees(), Employee.class))
        
    .and(route(GET("/employees/{id}"), 
      req -> ok().body(
        employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class)))
        
    .and(route(POST("/employees/update"), 
      req -> req.body(toMono(Employee.class))
        .doOnNext(employeeRepository()::updateEmployee)
        .then(ok().build())));
}

Здесь мы использовали функцию Router.и() для объединения наших маршрутов.

Наконец, мы внедрили полный REST API, необходимый для нашего приложения Управление сотрудниками , используя маршрутизаторы и обработчики.

Для запуска приложения мы можем использовать либо отдельные маршруты, либо единый, составленный маршрут, который мы создали выше.

6. Маршруты тестирования

Мы можем использовать Клиент веб-тестирования чтобы проверить наши маршруты.

Для этого нам сначала нужно связать маршруты с помощью метода bind To Router Function , а затем построить экземпляр тестового клиента.

Давайте протестируем наш getEmployeeById маршрут :

@Test
public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() {
    WebTestClient client = WebTestClient
      .bindToRouterFunction(config.getEmployeeByIdRoute())
      .build();

    Employee employee = new Employee("1", "Employee 1");

    given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee));

    client.get()
      .uri("/employees/1")
      .exchange()
      .expectStatus()
      .isOk()
      .expectBody(Employee.class)
      .isEqualTo(employee);
}

и аналогично getAllEmployees Route :

@Test
public void whenGetAllEmployees_thenCorrectEmployees() {
    WebTestClient client = WebTestClient
      .bindToRouterFunction(config.getAllEmployeesRoute())
      .build();

    List employees = Arrays.asList(
      new Employee("1", "Employee 1"),
      new Employee("2", "Employee 2"));

    Flux employeeFlux = Flux.fromIterable(employees);
    given(employeeRepository.findAllEmployees()).willReturn(employeeFlux);

    client.get()
      .uri("/employees")
      .exchange()
      .expectStatus()
      .isOk()
      .expectBodyList(Employee.class)
      .isEqualTo(employees);
}

Мы также можем протестировать наш обновить маршрут сотрудника , утверждая, что наш Экземпляр сотрудника обновляется через EmployeeRepository :

@Test
public void whenUpdateEmployee_thenEmployeeUpdated() {
    WebTestClient client = WebTestClient
      .bindToRouterFunction(config.updateEmployeeRoute())
      .build();

    Employee employee = new Employee("1", "Employee 1 Updated");

    client.post()
      .uri("/employees/update")
      .body(Mono.just(employee), Employee.class)
      .exchange()
      .expectStatus()
      .isOk();

    verify(employeeRepository).updateEmployee(employee);
}

Для получения более подробной информации о тестировании с помощью Web Test Client , пожалуйста, обратитесь к нашему учебнику по работе с WebClient и WebTestClient .

7. Резюме

В этом уроке мы представили новый функциональный веб – фреймворк весной 5 и рассмотрели его два основных интерфейса – RouterFunction и HandlerFunction. Мы также узнали, как создавать различные маршруты для обработки запроса и отправки ответа.

Кроме того, мы воссоздали наше приложение Управление сотрудниками , представленное в руководстве по Spring 5 WebFlux, с моделью функциональных конечных точек.

Как всегда, полный исходный код можно найти на Github .