邯郸建设网站公司,wordpress 如果,免费网站设计定制,网页制作报价模板WebClient是Spring Framework的反应式客户端#xff0c;用于进行服务到服务的调用。 WebClient已成为我的实用工具#xff0c;但是最近我意外地遇到了一个问题#xff0c;即它处理Java 8时间字段的方式使我绊倒了#xff0c;本文对此进行了详细介绍。 快乐之路 首先是幸福… WebClient是Spring Framework的反应式客户端用于进行服务到服务的调用。 WebClient已成为我的实用工具但是最近我意外地遇到了一个问题即它处理Java 8时间字段的方式使我绊倒了本文对此进行了详细介绍。 快乐之路 首先是幸福的道路。 使用WebClient时 Spring Boot建议将“ WebClient.Builder”注入到类中而不是“ WebClient”本身并且已经自动配置了WebClient.Builder并可以注入。 考虑一个虚拟的“城市”域和一个创建“城市”的客户。 “城市”具有简单的结构请注意creationDate是Java8“即时”类型 import java.time.Instant data class City( val id: Long, val name: String, val country: String, val pop: Long, val creationDate: Instant Instant.now() ) 用于创建此类型实例的客户端如下所示 CitiesClient( class CitiesClient( private val webClientBuilder: WebClient.Builder, private val citiesBaseUrl: String ) { fun createCity(city: City): MonoCity { val uri: URI UriComponentsBuilder .fromUriString(citiesBaseUrl) .path( /cities ) .build() .encode() .toUri() val webClient: WebClient this .webClientBuilder.build() return webClient.post() .uri(uri) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .bodyValue(city) .exchange() .flatMap { clientResponse - clientResponse.bodyToMono(City:: class .java) } } } 了解如何以一种流畅的方式表达意图。 首先设置uri和标头然后放置请求主体然后将响应解组回“ City”响应类型。 一切都很好。 现在测试看起来如何。 我正在使用出色的Wiremock来启动虚拟远程服务并使用此CitiesClient发送请求方法如下 SpringBootTest AutoConfigureJson WebClientConfigurationTest { class WebClientConfigurationTest { Autowired private lateinit var webClientBuilder: WebClient.Builder Autowired private lateinit var objectMapper: ObjectMapper Test fun testAPost() { val dateAsString 1985-02-01T10:10:10Z val city City( id 1L, name some city , country some country , pop 1000L, creationDate Instant.parse(dateAsString) ) WIREMOCK_SERVER.stubFor( post(urlMatching( /cities )) .withHeader( Accept , equalTo( application/json )) .withHeader( Content-Type , equalTo( application/json )) .willReturn( aResponse() .withHeader( Content-Type , application/json ) .withStatus(HttpStatus.CREATED.value()) .withBody(objectMapper.writeValueAsString(city)) ) ) val citiesClient CitiesClient(webClientBuilder, http://localhost: ${WIREMOCK_SERVER.port()} ) val citiesMono: MonoCity citiesClient.createCity(city) StepVerifier .create(citiesMono) .expectNext(city) .expectComplete() .verify() //Ensure that date field is in ISO-8601 format.. WIREMOCK_SERVER.verify( postRequestedFor(urlPathMatching( /cities )) .withRequestBody(matchingJsonPath( $.creationDate , equalTo(dateAsString))) ) } companion object { private val WIREMOCK_SERVER WireMockServer(WireMockConfiguration.wireMockConfig().dynamicPort().notifier(ConsoleNotifier( true ))) BeforeAll JvmStatic fun beforeAll() { WIREMOCK_SERVER.start() } AfterAll JvmStatic fun afterAll() { WIREMOCK_SERVER.stop() } } } 在突出显示的行中我要确保远程服务以ISO-8601格式接收日期为“ 1985-02-01T101010Z”。 在这种情况下一切正常进行测试通过了。 不太开心的路 现在考虑以某种形式自定义WebClient.Builder的情况。 一个例子是说我正在使用注册表服务并且我想通过此注册表查找远程服务然后打电话然后必须自定义WebClient以在其上添加“ LoadBalanced”注释- 这里有一些详细信息 可以这么说我以这种方式自定义了WebClient.Builder Configuration WebClientConfiguration { class WebClientConfiguration { Bean fun webClientBuilder(): WebClient.Builder { return WebClient.builder().filter { req, next - LOGGER.error( Custom filter invoked.. ) next.exchange(req) } } companion object { val LOGGER loggerForWebClientConfiguration() } } 它看起来很简单但是现在以前的测试失败了。 具体来说网上的creationDate的日期格式不再是ISO-8601原始请求如下所示 { id : 1 , name : some city , country : some country , pop : 1000 , creationDate : 476100610.000000000 } 与工作要求 { id : 1 , name : some city , country : some country , pop : 1000 , creationDate : 1985-02-01T10:10:10Z } 查看日期格式有何不同。 问题 这个问题的根本原因很简单Spring Boot在WebClient.Builder上添加了一堆配置当我自己明确创建bean时这些配置会丢失。 特别是在这种情况下在后台创建了一个Jackson ObjectMapper默认情况下将日期写为时间戳– 此处有一些详细信息。 解 好的那么我们如何取回Spring Boot进行的自定义。 我实质上已经在Spring中复制了称为“ WebClientAutoConfiguration”的自动配置的行为它看起来像这样 Configuration WebClientConfiguration { class WebClientConfiguration { Bean fun webClientBuilder(customizerProvider: ObjectProviderWebClientCustomizer): WebClient.Builder { val webClientBuilder: WebClient.Builder WebClient .builder() .filter { req, next - LOGGER.error( Custom filter invoked.. ) next.exchange(req) } customizerProvider.orderedStream() .forEach { customizer - customizer.customize(webClientBuilder) } return webClientBuilder; } companion object { val LOGGER loggerForWebClientConfiguration() } } 除了复制这种行为可能还有更好的方法但是这种方法对我有用。 现在发布的内容如下所示 { id : 1 , name : some city , country : some country , pop : 1000 , creationDate : 1985-02-01T10:10:10Z } 日期以正确的格式显示。 结论 Spring Boot对WebClient的自动配置提供了一套明确的默认值。 如果出于任何原因需要显式配置WebClient及其构建器请警惕Spring Boot添加的一些自定义项并将其复制为自定义bean。 在我的案例中我的自定义“ WebClient.Builder”中缺少针对Java 8日期的Jackson定制因此必须明确说明。 此处提供示例测试和自定义 翻译自: https://www.javacodegeeks.com/2020/01/spring-webclient-and-java-date-time-fields.html