響應式書店服務代理

工程 | Roy Clarkson | 2020年1月14日 | ...

祝 Spring 社群新年快樂!在新的一年裡,我們將繼續在 Spring 專案生態系統中進行令人驚歎的開發和進步,我想與大家分享一個更新的示例應用程式,它代表了我們在整個產品組合中為支援響應式程式設計模型所取得的一些進展。

BookStore 服務代理示例應用已更新,以演示多個 Spring 專案的整合,包括 Spring Cloud Open Service Broker、Spring Data、Spring Security、Spring HATEOAS,當然還有 Spring WebFlux 和 Spring Boot。所有這些專案都有包含響應式支援的 GA 版本,可用於您自己的應用和服務中。

為簡單起見,該應用程式本身既充當服務代理又充當服務例項。雖然服務代理本身遵循開放服務代理 API,但它們提供的服務定義更為抽象。服務可以做或成為任何東西。在此應用程式中,為每個提供的服務例項建立一組新的憑據。這些憑據用於向服務例項發出請求。新服務例項的 URL 配置為與服務代理本身的路由相同。透過這種方式,憑據用於區分對各種服務例項的請求。目標是設計一個自包含的綜合示例,演示 Spring 組合的許多部分。

Spring Cloud Open Service Broker 3.1

Spring Cloud Open Service Broker 是一個用於構建 Spring Boot 應用程式的框架,它實現了開放服務代理 API,允許開發人員將服務交付給在 Cloud Foundry、Kubernetes 和 OpenShift 等雲原生平臺上執行的應用程式。自 3.0 版本以來,Spring Cloud Open Service Broker 透過控制器和服務介面中的響應式型別支援 Spring WebFlux 和 Spring MVC Web 框架。

要開始使用 Spring Cloud Open Service Broker,請在您的應用程式中包含 Spring Boot 啟動器

implementation('org.springframework.cloud:spring-cloud-starter-open-service-broker:3.1.0.RELEASE')

接下來,實現 ServiceInstanceServiceServiceInstanceBindingService。以下程式碼說明了所需的 API。檢視示例應用程式以獲取完整詳細資訊。

@Service
public class BookStoreServiceInstanceService
		implements ServiceInstanceService {

	@Override
	public Mono<CreateServiceInstanceResponse> createServiceInstance(
		CreateServiceInstanceRequest request) {...}

	@Override
	public Mono<GetServiceInstanceResponse> getServiceInstance(
		GetServiceInstanceRequest request) {...}

	@Override
	public Mono<DeleteServiceInstanceResponse> deleteServiceInstance(
		DeleteServiceInstanceRequest request) {...}

}

Spring Data Moore

Spring Data 釋出列最初在 Spring Data Kay 中引入了響應式支援。Spring Data R2DBC 最近釋出了 GA 版本,但 Spring Boot 尚未釋出與 Spring Data R2DBC 整合的 GA 版本。本示例使用 MongoDB 作為後端資料儲存。

要開始使用響應式 MongoDB,請新增 Spring Boot 啟動器

implementation('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')

為了演示目的,新增一個嵌入式 MongoDB 伺服器

implementation('de.flapdoodle.embed:de.flapdoodle.embed.mongo')

接下來,配置響應式儲存庫

@Configuration
@EnableReactiveMongoRepositories(basePackageClasses = {
		ServiceBrokerRepositoryPackageMarker.class,
		WebRepositoryPackageMarker.class
})
public class ApplicationRepositoryConfiguration {
}

最後,定義一個 ReactiveCrudRepository。以下介面是示例應用程式中的一個示例

public interface ServiceInstanceRepository extends ReactiveCrudRepository<ServiceInstance, String> {
}

Spring Security 5.2

響應式支援最初包含在 Spring Security 5 中,與 Spring Boot 和 Spring Framework 的整合持續成熟。

要使用 Spring Security,請包含 Spring Boot 啟動器

implementation('org.springframework.boot:spring-boot-starter-security')

接下來,使用 @EnableWebFluxSecurity 定義安全配置。此程式碼說明了該應用程式如何保護 /v2 處的服務代理端點以及響應服務例項請求的 /bookstores 端點

@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {

	@Bean
	public SecurityWebFilterChain securityWebFilterChain(
		ServerHttpSecurity http) {
		return http
				.csrf().disable()
				.httpBasic()
				.and().authorizeExchange()
				.pathMatchers("/bookstores/**").authenticated()
				.pathMatchers("/v2/**").hasAuthority(
					SecurityAuthorities.ADMIN)
				.matchers(EndpointRequest.to("info", "health")).permitAll()
				.matchers(EndpointRequest.toAnyEndpoint()).hasAuthority(
					SecurityAuthorities.ADMIN)
				.and().build();
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

}

接下來,實現一個 ReactiveUserDetailsService

@Service
public class RepositoryUserDetailsService implements
		ReactiveUserDetailsService {

	private final UserRepository userRepository;

	public RepositoryUserDetailsService(UserRepository userRepository) {
		this.userRepository = userRepository;
	}

}

最後,WebFlux 控制器不支援許可權評估器,但我們可以透過在 SpEL 表示式中呼叫一個 Bean 並傳遞 Authentication 物件來實現類似的功能

@GetMapping("/{bookStoreId}")
@PreAuthorize("hasAnyRole('ROLE_FULL_ACCESS','ROLE_READ_ONLY') and
@bookStoreIdEvaluator.canAccessBookstore(authentication, #bookStoreId)")
public Mono<ResponseEntity<BookStoreResource>> getBooks(
	@PathVariable String bookStoreId) {
	return bookStoreService.getBookStore(bookStoreId)
			.flatMap(this::createResponse);
}

在此示例中,解析許可權以確定是否存在書店 ID

public boolean canAccessBookstore(Authentication authentication,
		String bookStoreId) {
	return authentication.getAuthorities().stream()
			.filter(authority -> authority.getAuthority()
					.startsWith(BOOK_STORE_ID_PREFIX))
			.map(authority -> {
				String serviceInstanceId = authority.getAuthority()
						.substring(BOOK_STORE_ID_PREFIX.length());
				return serviceInstanceId.equals(bookStoreId);
			})
			.findFirst()
			.orElse(true);
}

Spring HATEOAS 1.0

Spring HATEOAS 1.0 GA 最近釋出,其中包括對連結建立和表示建模的響應式支援。

包含 Spring HATEOAS 啟動器以啟用 Spring Boot 自動配置。因為我們正在構建一個響應式 Spring WebFlux 應用程式,所以我們需要排除 Spring Web 啟動器

implementation('org.springframework.boot:spring-boot-starter-hateoas') {
		exclude group: 'org.springframework.boot', module: 'spring-boot-starter-web'
}

接下來,您可以使用 WebFluxLinkBuilder 來組裝超媒體資源

public Mono<BookResource> toModel(Book book, String bookStoreId) {
		return Mono.just(new BookResource(book))
				.flatMap(bookResource -> linkTo(methodOn(
					BookController.class).getBook(bookStoreId, book.getId()))
						.withSelfRel()
						.toMono()
						.flatMap(link -> Mono.just(bookResource.add(link)))
						.thenReturn(bookResource));
	}

然後您可以在控制器響應體中使用該資源

return new BookStoreResourceAssembler().toModel(bookStore)
		.flatMap(bookStoreResource -> Mono.just(new ResponseEntity<>(bookStoreResource, HttpStatus.OK)));

Spring Framework 5.2

Spring Framework 5 最初在新的 Spring WebFlux Web 框架中提供了響應式支援。此外,新的 WebClientWebTestClient 包括對消費和測試 Spring WebFlux 應用程式的支援。

要使用這些,只需包含 Spring Boot 啟動器

implementation('org.springframework.boot:spring-boot-starter-webflux')
testImplementation('org.springframework.boot:spring-boot-starter-test') {
	exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}

例如,使用 WebTestClient 來驗證控制器功能

this.client.get().uri("/bookstores/{bookStoreId}", bookStoreId)
		.accept(MediaType.APPLICATION_JSON)
		.exchange()
		.expectStatus().isEqualTo(HttpStatus.OK);

Spring Boot 2.2

Spring Boot 透過為 Spring WebFlux、Spring Data、Spring Security、Spring HATEOAS 中的響應式支援以及測試工具提供自動配置,將所有這些專案整合在一起。在許多情況下,實現僅需要包含特定的 Spring Boot Starters 或相關依賴項即可啟用功能。

結論

本文簡要介紹了 Spring 專案中響應式支援。隨著更廣泛的 Spring 組合繼續採用和支援響應式 API,開發人員將有更多選擇,決定在其應用程式中使用命令式還是響應式程式設計風格。此外,此示例應用程式僅演示了目前提供響應式支援的 Spring 專案的一個子集。未來請期待更多!如果您對特定的 Spring 專案有疑問或問題,請在相關專案的 GitHub 頁面上聯絡維護人員。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有