Spring Cloud Gateway整合Feign时时间格式序列化问题解决方案

一、起因

feign调用的地方如下:
Spring Cloud Gateway整合Feign时时间格式序列化问题解决方案_第1张图片
因为是在Spring cloud gateway环境(版本:3.1.4),所以肯定与webflux有关系,因为原来的cloud都是与servlet相关的,现在是响应式的,默认都是webflux的那一套。所以,报错了。

2022-11-26 15:59:02.462 [http-nio-9080-exec-10] ERROR o.s.b.a.w.r.error.AbstractErrorWebExceptionHandler [102]- [6b570600]  500 Server Error for HTTP POST "/user/doLogin"
feign.codec.EncodeException: No qualifying bean of type 'org.springframework.boot.autoconfigure.http.HttpMessageConverters' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at feign.ReflectiveFeign$BuildEncodedTemplateFromArgs.resolve(ReflectiveFeign.java:401)
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ springfox.boot.starter.autoconfigure.SwaggerUiWebFluxConfiguration$CustomWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ cn.dev33.satoken.reactor.filter.SaReactorFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP POST "/user/doLogin" [ExceptionHandlingWebHandler]

二、解决HttpMessageConvert问题

加上以下代码:

    @Bean
    @ConditionalOnMissingBean
    public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
        return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
    }

但是,时间序列化问题来了。

2022-11-26 16:28:40.216 [http-nio-9080-exec-1] ERROR o.s.b.a.w.r.error.AbstractErrorWebExceptionHandler [102]- [4e5133ce]  500 Server Error for HTTP POST "/user/doLogin"
feign.codec.DecodeException: Error while extracting response for type [com.taeyeon.im.common.bean.response.ResultEntity<com.taeyeon.im.common.bean.response.TaeUserResponse>] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String "2022-11-14 16:53:44": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2022-11-14 16:53:44' could not be parsed at index 10; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDateTime` from String "2022-11-14 16:53:44": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2022-11-14 16:53:44' could not be parsed at index 10
 at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 208] (through reference chain: com.taeyeon.im.common.bean.response.ResultEntity["data"]->com.taeyeon.im.common.bean.response.TaeUserResponse["createTime"])
	at feign.AsyncResponseHandler.decode(AsyncResponseHandler.java:119)
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ springfox.boot.starter.autoconfigure.SwaggerUiWebFluxConfiguration$CustomWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ cn.dev33.satoken.reactor.filter.SaReactorFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP POST "/user/doLogin" [ExceptionHandlingWebHandler]
Original Stack Trace:
		at feign.AsyncResponseHandler.decode(AsyncResponseHandler.java:119)
		at feign.AsyncResponseHandler.handleResponse(AsyncResponseHandler.java:87)
		at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:138)
		at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:89)
		at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100)
		at com.sun.proxy.$Proxy104.login(Unknown Source)
		at com.taeyeon.im.gw.api.UserController.doLogin(UserController.java:39)
		at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
		at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
		at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
		at java.lang.reflect.Method.invoke(Method.java:498)
		at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$invoke$0(InvocableHandlerMethod.java:144)
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125)
		at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
		at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:251)
		at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:336)
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180)
		at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:101)
		at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200)
		at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
		at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
		at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:151)
		at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299)
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)
		at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
		at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:160)
		at org.springframework.http.server.reactive.AbstractListenerReadPublisher$State.onAllDataRead(AbstractListenerReadPublisher.java:484)
		at org.springframework.http.server.reactive.AbstractListenerReadPublisher.onAllDataRead(AbstractListenerReadPublisher.java:134)
		at org.springframework.http.server.reactive.ServletServerHttpRequest$RequestBodyPublisher$RequestBodyPublisherReadListener.onAllDataRead(ServletServerHttpRequest.java:347)
		at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:218)
		at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:241)
		at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:52)
		at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890)
		at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789)
		at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
		at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
		at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
		at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
		at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.web.client.RestClientException: Error while extracting response for type [com.taeyeon.im.common.bean.response.ResultEntity<com.taeyeon.im.common.bean.response.TaeUserResponse>] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String "2022-11-14 16:53:44": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2022-11-14 16:53:44' could not be parsed at index 10; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDateTime` from String "2022-11-14 16:53:44": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2022-11-14 16:53:44' could not be parsed at index 10
 at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 208] (through reference chain: com.taeyeon.im.common.bean.response.ResultEntity["data"]->com.taeyeon.im.common.bean.response.TaeUserResponse["createTime"])
	at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:120)
	at org.springframework.cloud.openfeign.support.SpringDecoder.decode(SpringDecoder.java:75)
	at org.springframework.cloud.openfeign.support.ResponseEntityDecoder.decode(ResponseEntityDecoder.java:61)
	at feign.optionals.OptionalDecoder.decode(OptionalDecoder.java:36)
	at feign.AsyncResponseHandler.decode(AsyncResponseHandler.java:115)
	at feign.AsyncResponseHandler.handleResponse(AsyncResponseHandler.java:87)
	at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:138)
	at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:89)
	at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100)
	at com.sun.proxy.$Proxy104.login(Unknown Source)
	at com.taeyeon.im.gw.api.UserController.doLogin(UserController.java:39)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$invoke$0(InvocableHandlerMethod.java:144)
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125)
	at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
	at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:251)
	at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:336)
	at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180)
	at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:101)
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200)
	at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
	at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
	at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:151)
	at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)
	at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299)
	at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)
	at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816)
	at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:160)
	at org.springframework.http.server.reactive.AbstractListenerReadPublisher$State.onAllDataRead(AbstractListenerReadPublisher.java:484)
	at org.springframework.http.server.reactive.AbstractListenerReadPublisher.onAllDataRead(AbstractListenerReadPublisher.java:134)
	at org.springframework.http.server.reactive.ServletServerHttpRequest$RequestBodyPublisher$RequestBodyPublisherReadListener.onAllDataRead(ServletServerHttpRequest.java:347)
	at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:218)
	at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:241)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:52)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String "2022-11-14 16:53:44": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2022-11-14 16:53:44' could not be parsed at index 10; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDateTime` from String "2022-11-14 16:53:44": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2022-11-14 16:53:44' could not be parsed at index 10
 at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 208] (through reference chain: com.taeyeon.im.common.bean.response.ResultEntity["data"]->com.taeyeon.im.common.bean.response.TaeUserResponse["createTime"])
	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:391)
	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:343)
	at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:105)
	... 44 common frames omitted
Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDateTime` from String "2022-11-14 16:53:44": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2022-11-14 16:53:44' could not be parsed at index 10
 at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 208] (through reference chain: com.taeyeon.im.common.bean.response.ResultEntity["data"]->com.taeyeon.im.common.bean.response.TaeUserResponse["createTime"])
	at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1991)
	at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:1219)
	at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._handleDateTimeException(JSR310DeserializerBase.java:176)
	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer._fromString(LocalDateTimeDeserializer.java:179)
	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer.deserialize(LocalDateTimeDeserializer.java:81)
	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer.deserialize(LocalDateTimeDeserializer.java:40)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:392)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:392)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3682)
	at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:380)
	... 46 common frames omitted
Caused by: java.time.format.DateTimeParseException: Text '2022-11-14 16:53:44' could not be parsed at index 10
	at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
	at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
	at java.time.LocalDateTime.parse(LocalDateTime.java:492)
	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer._fromString(LocalDateTimeDeserializer.java:177)
	... 58 common frames omitted

搜咯一大堆,都是解决无gateway的Springboot feign调用的序列化问题。可以参考这篇文章
feign请求返回值反序列LocalDateTime异常记录

三、解决Spring Cloud Gateway时间序列化问题

结合以上解决方案,我处理了下HttpMessageConverters

 	@Bean
    @ConditionalOnMissingBean
    public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
        List<HttpMessageConverter<?>> list = converters.orderedStream().collect(Collectors.toList());
        list.add(mappingJackson2HttpMessageConverter());
        return new HttpMessageConverters(list);
    }

    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        return new MappingJackson2HttpMessageConverter(objectMapper());
    }

将自己定义的objectMapper包装成HttpMessageConverter,再加入到messageConverters中,我的objectMapper这样写的

	@Bean
    @Primary
    public ObjectMapper objectMapper() {
        ObjectMapper om = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(Date.class, new DateSerializer());
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));
        javaTimeModule.addDeserializer(Date.class, new DateDeserializer());
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));
        om.registerModule(javaTimeModule);
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        om.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
        return om;
    }

    public static class DateSerializer extends JsonSerializer<Date> {
        @Override
        public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(DateUtil.format(date, DatePattern.NORM_DATETIME_FORMAT));
        }
    }

    public static class DateDeserializer extends JsonDeserializer<Date> {
        @SneakyThrows
        @Override
        public Date deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException {
            return DateUtil.parseDateTime(p.getValueAsString());
        }
    }

以上就是我的解决方案。

你可能感兴趣的:(Spring,Spring,Cloud,微服务,java,spring,cloud,开发语言)