1前書き

Spring WebFluxフレームワークは反応原理を使って構築された新しい機能的Webフレームワークを導入します

このチュートリアルでは、このフレームワークを実際に使用する方法を学びます。

これは、既存のチュートリアルhttps://www.baeldung.com/spring-webflux[Spring 5 WebFluxのガイド]に基づいています。そのガイドでは、Annotationベースのコンポーネントを使用して小さなリアクティブRESTアプリケーションを作成しました。ここでは、代わりに機能的フレームワークを使用します。


2 Mavenの依存関係

同じ依存関係が必要です。https://search.maven.org/classic#search%7Cga%7C1%7Ca%3A%22spring-boot-starter-webflux%22%20AND%20g%3A%22org.springframework.boot前の記事で定義された%22[spring-boot-starter-webflux]:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>


3機能的Webフレームワーク

機能的Webフレームワークは、リクエストをルーティングし処理するために関数を使用する新しいプログラミングモデルを導入します。

アノテーションマッピングを使用するアノテーションベースのモデルとは対照的に、ここではhttp://docs.spring.io/spring/docs/5.0.0.M5/javadoc-api/org/springframework/web/reactiveを使用します。/function/server/HandlerFunction.html[

HandlerFunction

]および


http://docs.spring.io/spring/docs/5.0.0.M5/javadoc-api/org/springframework/web/reactive/function/server/RouterFunction。

html[RouterFunctions]

また、機能的Webフレームワークは、注釈ベースのリアクティブフレームワークが構築されたのと同じリアクティブスタック上に構築されます。



3.1. HandlerFunction



HandlerFunction

は、それらにルーティングされた要求に対する応答を生成する関数を表します。

@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
    Mono<T> handle(ServerRequest request);
}

このインタフェースは主に

Function <Request、Response <T>>

であり、サーブレットのように動作します。

ただし、標準のサーブレット

Servlet.service(ServletRequest req、ServletResponse res)

と比較すると、

HandlerFunction

は、応答を無料にしてテストおよび再利用を容易にするパラメータとして受け取る代わりに、応答を返します。


3.2.

RouterFunction



RouterFunction

は、@ @ RequestMapping__アノテーションの代替として機能します。受信リクエストをハンドラ関数にルーティングするために使用されます。

@FunctionalInterface
public interface RouterFunction<T extends ServerResponse> {
    Mono<HandlerFunction<T>> route(ServerRequest request);
   //...
}

通常は、__http://docs.spring.io/spring/docs/5.0.0.M5/javadoc-api/org/springframework/web/reactive/function/server/RouterFunctions.html#route-org.springframeworkをインポートできます。 .web.reactive.function.server.RequestPredicate-org.springframework.web.reactive.function.server.HandlerFunction-[RouterFunctions.route()]、完全なルーター関数を書くのではなく、ルートを作成するためのヘルパー関数。

それは__RequestPredicateを適用することでリクエストをルーティングすることを可能にします。

public static <T extends ServerResponse> RouterFunction<T> route(
  RequestPredicate predicate,
  HandlerFunction<T> handlerFunction)


RouterFunction

を返すことで、

route()

を連鎖させて入れ子にして、強力で複雑なルーティング方式を構築できます。

4.機能的Webを利用したリアクティブRESTアプリケーション


Spring WebFluxチュートリアルの手引き

で、注釈付きの

__ @ RestController


and

WebClientを使用して、小さな

__EmployeeManagement


RESTアプリケーションを作成します。

それでは、

Router

関数と

Handler

関数を使って同じアプリケーションを作成しましょう。

まず始めに、


私たちは

RouterFunction

を使用してルートを作成し、

Employeeのリアクティブストリームを公開および消費します。



ルートはSpringのBeanとして登録されており、Springの設定として使用される任意のクラス内に作成できます。

4.1. 単一リソース

単一の

Employee

リソースを公開する

__RouterFunction

__を使用して最初のルートを作成しましょう。

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

分解するために、最初の引数は

handler.

I.eを呼び出すHTTP GETリクエストを定義します。パスが

__/employee/\ {id}


で、要求がGET型の場合にのみ、


employeeRepositoryから一致する

Employee__を返します。

4.2. 収集リソース

次に、コレクションリソースを公開するために別のルートを追加しましょう。

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

4.3. 単一リソースの更新

最後に、

__従業員

__リソースを更新するためのルートを追加しましょう。

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

5.ルートを作成する

  • 私達は単一のルーター機能で一緒にルートを構成することもできます。

上記で作成したルートを組み合わせてみましょう。

@Bean
RouterFunction<ServerResponse> 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())));
}

ここでは、

__RouterFunction.and()

__を使用してルートを結合しました。

最後に、

__E​​mployeeManagement


アプリケーションに必要な

Router



__Handlerを使用して、すべてのREST APIを作成しました。私たちのアプリケーションを実行するためには、異なるルートを使用することも、上記で作成した単一のルートを使用することもできます。

6.ルートをテストする

  • ルートをテストするために

    WebTestClient

    を使うことができます。 **


__WebTestClientを使用してルートをテストするには、


we


bindToRouterFunction

__を使用してルートをバインドし、テストクライアントインスタンスを構築する必要があります。


getEmployeeByIdRoute

をテストしましょう。

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

    Employee expected = 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(expected);
}

同様に

getAllEmployeesRoute

:

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

    List<Employee> employeeList = new ArrayList<>();

    Employee employee1 = new Employee("1", "Employee 1");
    Employee employee2 = new Employee("2", "Employee 2");

    employeeList.add(employee1);
    employeeList.add(employee2);

    Flux<Employee> employeeFlux = Flux.fromIterable(employeeList);
    given(employeeRepository.findAllEmployees()).willReturn(employeeFlux);

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

また、更新された

__Employee


instanceが

EmployeeRepository

を介して更新されていることをアサートすることで、

updateEmployeeRoute__をテストすることもできます。

@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);
}


WebTestClient

を使用したテストの詳細については、https://www.baeldung.com/spring-5-webclient[

WebClient

および

WebTestClient

を使用した作業]のチュートリアルを参照してください。


7. 概要

このチュートリアルでは、Spring 5で新しい機能的なWebフレームワークを紹介し、その2つのコア関数

__Router





Handler

__を調べ、要求を処理して応答を送信するためのさまざまな経路の作成方法を学びました。

また、https://www.baeldung.com/spring-webflux[Spring 5 WebFluxガイド]で紹介された

__EmployeeManagement

__applicationを新しいフレームワークで作り直しました。

その基礎を

Reactor

に置くと、リアクティブフレームワークはデータストアへのリアクティブアクセスに完全に光ります。残念ながら、

MongoDB

などのいくつかの

NoSQL

データベースを除いて、ほとんどのデータストアはまだそのようなリアクティブアクセスを提供していません。

いつものように、完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/spring-5-reactive-security[Githubでの使用]にあります。