日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Spring WebClient和Java日期时间字段

發(fā)布時(shí)間:2023/12/3 java 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring WebClient和Java日期时间字段 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

WebClient是Spring Framework的反應(yīng)式客戶(hù)端,用于進(jìn)行服務(wù)到服務(wù)的調(diào)用。

WebClient已成為我的實(shí)用工具,但是最近我意外地遇到了一個(gè)問(wèn)題,即它處理Java 8時(shí)間字段的方式使我絆倒了,本文對(duì)此進(jìn)行了詳細(xì)介紹。

快樂(lè)之路

首先是幸福的道路。 使用WebClient時(shí), Spring Boot建議將“ WebClient.Builder”注入到類(lèi)中,而不是“ WebClient”本身,并且已經(jīng)自動(dòng)配置了WebClient.Builder并可以注入。

考慮一個(gè)虛擬的“城市”域和一個(gè)創(chuàng)建“城市”的客戶(hù)。 “城市”具有簡(jiǎn)單的結(jié)構(gòu),請(qǐng)注意creationDate是Java8“即時(shí)”類(lèi)型:

import java.time.Instant data class City( val id: Long, val name: String, val country: String, val pop: Long, val creationDate: Instant = Instant.now() )

用于創(chuàng)建此類(lèi)型實(shí)例的客戶(hù)端如下所示:

CitiesClient( class CitiesClient( private val webClientBuilder: WebClient.Builder, private val citiesBaseUrl: String ) { fun createCity(city: City): Mono<City> { 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) } } }

了解如何以一種流暢的方式表達(dá)意圖。 首先設(shè)置uri和標(biāo)頭,然后放置請(qǐng)求主體,然后將響應(yīng)解組回“ City”響應(yīng)類(lèi)型。

一切都很好。 現(xiàn)在測(cè)試看起來(lái)如何。

我正在使用出色的Wiremock來(lái)啟動(dòng)虛擬遠(yuǎn)程服務(wù),并使用此CitiesClient發(fā)送請(qǐng)求,方法如下:

@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: Mono<City> = 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() } } }

在突出顯示的行中,我要確保遠(yuǎn)程服務(wù)以ISO-8601格式接收日期為“ 1985-02-01T10:10:10Z”。 在這種情況下,一切正常進(jìn)行,測(cè)試通過(guò)了。

不太開(kāi)心的路

現(xiàn)在考慮以某種形式自定義WebClient.Builder的情況。 一個(gè)例子是說(shuō)我正在使用注冊(cè)表服務(wù),并且我想通過(guò)此注冊(cè)表查找遠(yuǎn)程服務(wù),然后打電話,然后必須自定義WebClient以在其上添加“ @LoadBalanced”注釋- 這里有一些詳細(xì)信息

可以這么說(shuō),我以這種方式自定義了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 = loggerFor<WebClientConfiguration>() } }

它看起來(lái)很簡(jiǎn)單,但是現(xiàn)在以前的測(cè)試失敗了。 具體來(lái)說(shuō),網(wǎng)上的creationDate的日期格式不再是ISO-8601,原始請(qǐng)求如下所示:

{ "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" }

查看日期格式有何不同。

問(wèn)題

這個(gè)問(wèn)題的根本原因很簡(jiǎn)單,Spring Boot在WebClient.Builder上添加了一堆配置,當(dāng)我自己明確創(chuàng)建bean時(shí),這些配置會(huì)丟失。 特別是在這種情況下,在后臺(tái)創(chuàng)建了一個(gè)Jackson ObjectMapper,默認(rèn)情況下將日期寫(xiě)為時(shí)間戳– 此處有一些詳細(xì)信息。

好的,那么我們?nèi)绾稳』豐pring Boot進(jìn)行的自定義。 我實(shí)質(zhì)上已經(jīng)在Spring中復(fù)制了稱(chēng)為“ WebClientAutoConfiguration”的自動(dòng)配置的行為,它看起來(lái)像這樣:

@Configuration WebClientConfiguration { class WebClientConfiguration { @Bean fun webClientBuilder(customizerProvider: ObjectProvider<WebClientCustomizer>): 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 = loggerFor<WebClientConfiguration>() } }

除了復(fù)制這種行為,可能還有更好的方法,但是這種方法對(duì)我有用。

現(xiàn)在發(fā)布的內(nèi)容如下所示:

{ "id" : 1 , "name" : "some city" , "country" : "some country" , "pop" : 1000 , "creationDate" : "1985-02-01T10:10:10Z" }

日期以正確的格式顯示。

結(jié)論

Spring Boot對(duì)WebClient的自動(dòng)配置提供了一套明確的默認(rèn)值。 如果出于任何原因需要顯式配置WebClient及其構(gòu)建器,請(qǐng)警惕Spring Boot添加的一些自定義項(xiàng)并將其復(fù)制為自定義bean。 在我的案例中,我的自定義“ WebClient.Builder”中缺少針對(duì)Java 8日期的Jackson定制,因此必須明確說(shuō)明。

此處提供示例測(cè)試和自定義

翻譯自: https://www.javacodegeeks.com/2020/01/spring-webclient-and-java-date-time-fields.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的Spring WebClient和Java日期时间字段的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。