Spring WebClient和Java日期时间字段

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): 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) } }  } 

了解如何以一种流畅的方式表达意图。 首先设置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: 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() } }  } 

在突出显示的行中,我要确保远程服务以ISO-8601格式接收日期为“ 1985-02-01T10:10:10Z”。 在这种情况下,一切正常进行,测试通过了。

不太开心的路

现在考虑以某种形式自定义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 = loggerFor<WebClientConfiguration>() }  } 

它看起来很简单,但是现在以前的测试失败了。 具体来说,网上的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: 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>() }  } 

除了复制这种行为,可能还有更好的方法,但是这种方法对我有用。

现在发布的内容如下所示:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/340325.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2台电脑一根网线传文件_Iphone 和PC如何共享文件

iphone上有个隔空传送的功能&#xff0c;传文件非常方便&#xff0c;但是需要苹果全家桶才能使用&#xff0c;和个人pc如何传文件&#xff0c;特别是家里的主力台式机&#xff0c;需要传个视频文件&#xff0c;微信25m限制&#xff0c;插线也不太方便&#xff0c;能否和隔空传送…

如何将原图和json融合_双曲知识嵌入:如何将知识“融合”带入新空间?

知识图谱作为人类知识的结构化数据&#xff0c;是构建人工智能的基石。然而目前的知识图谱都是不完备的&#xff0c;所以需要将多个知识图谱融合以获得更完备的知识库。基于表示学习的知识关联作为知识图谱融合的新方法受到了许多关注。但知识关联模型面临着参数多、复杂性高、…

android viewgroup点击变色,Android ViewGroup事件分发

上篇文章已经分析了Android的Touch事件分发。如果没看的建议先看一下。Android View的Touch事件分发。接下来我们开始写几种场景&#xff0c;得出一个初步的执行顺序&#xff0c;然后我们按照这个顺序开始分析。首先我们自定义一个ViewGroup和一个View&#xff0c;然后重写相关…

qq数据泄露_真良心,腾讯这个app竟然能查账号泄露

最近有朋友都在分享腾讯手机管家&#xff0c;纷纷表示这是一个良心app&#xff0c;能查到自己账号泄露。仔细想一下&#xff0c;这么多年下来确实忘了自己都在什么平台或网站注册过账号&#xff0c;在黑客泛滥的今天&#xff0c;黑客很容易从一些平台窃取数据库&#xff0c;拿到…

vue 如何获取图片的原图尺寸_阳台洗衣机组合柜如何设计|尺寸规范|案例图片...

对于阳台装洗衣机来说&#xff0c;不只是简单的装修&#xff0c;还需要我们考虑水电、尺寸以及美观实用等问题&#xff0c;以免后期出现问题的时候会更加麻烦。接下来深圳装修网小编就为你们带来阳台洗衣机组合柜的案例赏析&#xff0c;以及装修的尺寸规范等内容&#xff0c;一…

print2flashwindows7旗舰版下载哪一个_JUJUMAO_MSDN原版 win 7 二合一 旗舰版32位 64位原版ISO镜像...

文件: F:\JUJUMAO_msdn_Win7_ultimate_x86_x64.iso大小:4.73G(5082120192 字节) MD5: AA4C7E80C52AC0DEDC757EF86CF8057BSHA1: 66505AD9424ED2D2B0DEDE7067917B708A67C7DDCRC32: 0E2FAB2C高速下载地址&#xff1a;https://jujumao.cowtransfer.com/s/3220a80131744fJUJUMAO_MSD…

lambda ::表达式_Lambda表达式和流API:基本示例

lambda ::表达式这篇博客文章包含基本Lambda表达式和Stream API示例的列表&#xff0c;我在2014年6月在Java用户组Politechnica Gedanensis &#xff08;格但斯克技术大学&#xff09;和Goyello的实时编码演示中使用了这些示例。 Lambda表达式 句法 最常见的示例&#xff1a; …

android readonly file system,安卓ROOT权限下“Read-only file sytem”解决办法

今天用安卓模拟器&#xff1a;BlueStacks&#xff0c;打开apk终端模拟器&#xff1a;Terminal&#xff0c;在shell操作命令的时候提示“Read-only file sytem”&#xff1a;第一种方法&#xff1a;在 Android 系统中&#xff0c;我们通过 adb 登录到 shell 进行操作时&#xff…

客制化键盘编程_客制化键盘劝退指南

客制化键盘劝退指南最近总感觉mac book pro的键盘不太好用&#xff0c;所以把家里用的杜伽K320拿到公司用了。这把键盘&#xff0c;樱桃的银轴&#xff0c;红轴的压力克数&#xff0c;更小的键程&#xff0c;用起来还是不错的。如此一来&#xff0c;家里的台式机没有键盘了&…

HTML5怎样设置站点,我是怎样让网站用上HTML5 Manifest

Manifest是用来做离线页面的&#xff0c;即使断网也能正常打开页面&#xff0c;用起来简单&#xff0c;但是在实际使用中存在以下问题&#xff1a;(1)如何自动缓存所有的页面的资源&#xff1f;因为manifest不能使用*通配符进行cache(2)如果网站资源更新&#xff0c;怎么让mani…

wallpaper怎么设置锁屏_Apple ID密码忘了怎么找回?丨如何让面容和指纹解锁立马失效?...

忘记Apple ID这种事肯定不少果粉都经历过&#xff0c;像小编也是如此&#xff0c;因为密码都比较复杂&#xff0c;如果太久没输入过密码&#xff0c;久而久之就忘了。哪一天突然需要用的时候&#xff0c;怎么想也想不起来。想不起来的话我们就别想了&#xff0c;直接重置密码就…

在Java中将时间单位转换为持续时间

java.util.concurrent.TimeUnit以给定的粒度单位表示Java中的持续时间&#xff0c;并提供了跨单位转换的实用方法。 java.util.concurrent.TimeUnit最早是在Java早期&#xff08;1.5&#xff09;引入的&#xff0c;但自那时以来已经被扩展了好几次。 在此博客文章中&#xff0c…

ajax contenttype详解_$.ajax中contentType: “application/json” 的用法详解

具体内容如下所示&#xff1a;$.ajax({type: httpMethod,cache:false,async:false,contentType: "application/json; charsetutf-8",dataType: "json",//返回值类型url: pathurl,data:jsonData,success: function(data){var resultData 返回码data.status…

在text html模版中写js,Rails3使用text/html内容类型而不是text/javascript呈现js.erb模板...

我正在使用3.0.0.beta3构建一个新的应用程序.我只是尝试将js.erb模板呈现给Ajax请求以执行以下操作(在publications_controller.rb中):def get_pubmed_dataentry Bio::PubMed.query(params[:pmid])# searches PubMed and get entrypublication Bio::MEDLINE.new(entry) # cre…

jq监听子元素被点击_vue开发app点击字母展示地区列表(兄弟组件之间联动)

下图这种地区搜索方式在很多app中都很常见&#xff0c;今天就使用vue框架中的 better-scroll 第三方包来实现页面滚动和点击侧边栏字母该字母开头的地区列表置顶功能。1、A子组件通过使用 this.$emit(事件名字&#xff0c;事件携带内容) 向外触发事件首先&#xff0c;在组件每个…

e-mobile帐号状态存在异常_一文掌握异常检测的实用方法 | 技术实践

作者 | Vegard Flovik译者 | Tianyu责编 | Jane出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09;【导读】今天这篇文章会向大家介绍几个有关机器学习和统计分析的技术和应用&#xff0c;并展示如何使用这些方法解决一些具体的异常检测和状态监控实例。相信对一些开发…

用Spring组成自定义注释

Java批注在2004年随Java 5一起引入&#xff0c;是一种将元数据添加到Java源代码中的方法。 如今&#xff0c;许多主要框架&#xff08;如Spring或Hibernate&#xff09;都严重依赖注释。 在本文中&#xff0c;我们将介绍一个非常有用的Spring功能&#xff0c;该功能允许我们基…

单片机sleep函数的头文件_单片机代码模块化设计思想浅谈

前言&#xff1a;前段时间分享的文章【单片机裸机代码框架设计思路】&#xff0c;很多读者给我留言&#xff0c;觉得很不错&#xff0c;对于初学者而言&#xff0c;这是一个进阶的技巧&#xff0c;对于我而言&#xff0c;这是对自己总结和表达能力的一个提升。本文章我们再谈谈…

html中显示数据库中的一条数据,如何使用html表显示数据库中的数据

我正在尝试在HTML表中显示数据库中的数据。主要问题是&#xff1a;该表未出现。IdStringArray// Mostrar dadosecho "{$databaseName visteon;$pdo new Pdo(mysql:host127.0.0.1;dbname . $databaseName, root, );$result $pdo->query(SHOW TABLES FROM . $databas…

jquery 字符串查找_JQuery、Vue等考点

一. 写出下面程序的运行结果for结果&#xff1a;1秒后一下子打印出5个5。当循环完成时才会轮到setTimeout异步执行其回调函数function&#xff0c;此时i已经变成5&#xff0c;故5个console.log(i)里的i全使用的是5。易错点&#xff1a;千万别写成“打印5个4”啊&#xff01;暴风…