响应式 BookStore 服务代理

工程 | 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 版本,并已准备好用于您自己的应用程序和服务中的生产环境。

为简单起见,应用程序本身充当服务代理和服务实例。虽然服务代理本身遵循 Open Service Broker API,但它们提供的服务定义更为抽象。服务可以做任何事情或成为任何东西。在本应用程序中,为每个配置的服务实例创建一组新的凭据。这些凭据用于服务实例的请求中。新服务实例的 URL 配置为与服务代理本身的路由相同。通过这种方式,凭据用于区分对各个服务实例的请求。目标是设计一个自包含的综合示例,以演示 Spring 产品组合的许多部分。

Spring Cloud Open Service Broker 3.1

Spring Cloud Open Service Broker 是一个用于构建 Spring Boot 应用程序的框架,这些应用程序实现了 Open Service Broker API,该 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 启动器或相关依赖项即可激活功能。

结论

本文简要介绍了某些 Spring 项目中的响应式支持。随着更广泛的 Spring 产品组合继续采用和支持响应式 API,开发人员将获得更多选择,可以选择在其应用程序中使用命令式或响应式样式编程。此外,此示例应用程序仅演示了现在提供响应式支持的 Spring 项目的子集。敬请期待未来更多内容!如果您对特定 Spring 项目有任何疑问或问题,请在相关项目 GitHub 页面上联系维护人员。

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部