【转】Building a Reactive RESTful Web Service

【From】https://spring.io/guides/gs/reactive-rest-service/

Building a Reactive RESTful Web Service —— 用 Spring WebFlux 构建reactive restful web服务

本文转自以上Pivotal公司原文,简要概括翻译。

使用spring framework 5 里面的webflux来构建一个最基本的restful web服务。本人实测使用环境是JDK1.8,springboot 2.2.1.RELEASE,gradle 5.2.1,IntelliJ IDEA 2019。

一、创建项目

在IDEA里创建一个新的gradle项目,例如取名“webflux_greeting”。在IDEA中建好项目会自己构建这个空项目,然后一直显示“configuring build...”,应该是一直连不了maven的官方仓库的问题,我直接关了。下面修改完build.gradle之后加上阿里云的maven仓库,再构建就好。

二、配置build.gradle

使用以下内容替换自动生成的build.gradle文件,buildscript里面的仓库信息是给gradle下载自己的依赖用的,buildscript后面的repositories才是项目构建时查找依赖的仓库地址,两个都加上阿里云仓库作为优先选择。

buildscript {
    ext {
        springBootVersion = '2.2.1.RELEASE'
    }
    repositories {
maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } mavenCentral() } dependencies { classpath(
"org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' bootJar { baseName = 'gs-reactive-rest-service' version = '0.1.0' } sourceCompatibility = 1.8 repositories {
maven { url "http://maven.aliyun.com/nexus/content/groups/public/" } mavenCentral() } dependencies { compile(
'org.springframework.boot:spring-boot-starter-webflux') testCompile('org.springframework.boot:spring-boot-starter-test') testCompile('io.projectreactor:reactor-test') }

Spring Boot gradle 插件提供了以下好处:

a、会自动收集所有需要的jar,打包成一个uber-jar,即集成所有依赖包。

b、自动识别所有 “public static void main()” 方法,并标记成可执行类。

c、自动解决项目中和springboot的引用相关的版本依赖和冲突,你也可以手动指定依赖版本。

三、编写WebFlux Handler

在IDEA的项目名上右键,选择添加目录,然后会弹出快捷方式。选择创建“src/main/java”目录,再在目录下添加包“hello”,然后创建一个文件 GreetingHandler.java:

package hello;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

import reactor.core.publisher.Mono;

@Component
public class GreetingHandler {

  public Mono<ServerResponse> hello(ServerRequest request) {
    return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
      .body(BodyInserters.fromValue("Hello, Spring!"));
  }
}

四、编写router

类似的方式,在hello包中添加新class GreetingRouter:

package hello;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
public class GreetingRouter {

    @Bean
    public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {
        return RouterFunctions
                .route(RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), greetingHandler::hello);
    }
}

五、编写client (可选)

类似的方式,在hello包中添加新class GreetingWebClient。这个客户端既可以访问reactive的restful endpoint,同样适用于访问non-reactive的阻塞restful服务。

package hello;

import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Mono;

public class GreetingWebClient {
    private WebClient client = WebClient.create("http://localhost:8080");

    private Mono<ClientResponse> result = client.get()
            .uri("/hello")
            .accept(MediaType.TEXT_PLAIN)
            .exchange();

    public String getResult() {
        return ">> result = " + result.flatMap(res -> res.bodyToMono(String.class)).block();
    }
}

六、添加springboot主类

类似的方式,在hello包中添加新class Application。此“@SpringBootApplication”的注解等效于 @Configuration + @EnableAutoConfiguration + @ComponentScan。

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

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

        GreetingWebClient gwc = new GreetingWebClient();
        System.out.println(gwc.getResult());
    }
}

七、构建项目

此时,执行gradle的build任务,项目会编译并运行,控制台会输出 >> result = Hello, Spring!,这是上面所写的客户端访问所得的结果。 如果从浏览器访问本地地址 http://localhost:8080/hello,同样可以得到 “Hello, Spring!” 的结果。

在项目路径下会生成build/libs目录,里面有打包好的用bootJar里设置好的名字和版本所生成的uber-jar “gs-reactive-rest-service-0.1.0.jar”,可以用 java -jar gs-reactive-rest-service-0.1.0.jar 来直接启动。

八、测试类

创建测试类 GreetingRouterTest.java :

package hello;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(SpringRunner.class)
//  We create a `@SpringBootTest`, starting an actual server on a `RANDOM_PORT`
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class GreetingRouterTest {

  // Spring Boot will create a `WebTestClient` for you,
  // already configure and ready to issue requests against "localhost:RANDOM_PORT"
  @Autowired
  private WebTestClient webTestClient;

  @Test
  public void testHello() {
    webTestClient
      // Create a GET request to test an endpoint
      .get().uri("/hello")
      .accept(MediaType.TEXT_PLAIN)
      .exchange()
      // and use the dedicated DSL to test assertions against the response
      .expectStatus().isOk()
      .expectBody(String.class).isEqualTo("Hello, Spring!");
  }
}
原文地址:https://www.cnblogs.com/pekkle/p/12240100.html