SpringCloudFeignJackson自定义配置
Spring Cloud Feign 默认支持 Spring MVC 的注解 使用相同的 HttpMessageConverters 类转换
官方文档说明:
Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web.
但是我们一般在返回给前端 JSON 格式的时候 都会把相应的 NULL 值转为相应的"",这使得 Spring Cloud Feign 也使用相同的 Jackson 配置,例如我们项目的配置
@Bean
public ObjectMapper jacksonObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator jg, SerializerProvider sp)
throws IOException, JsonProcessingException {
jg.writeString("");
sp.getDefaultNullKeySerializer();
}
});
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
return objectMapper;
}出现的问题: 在服务通过 Feign 进行请求的时候,传 NULL 值引用类型值时,会出现类型转换异常,由于 HttpMessageConverters 直接把 NULL 转为了""。
解决思路: 自定义配置 Spring Cloud Feign Encoder 与 Decoder
官方文档说明:
Spring Cloud Netflix provides the following beans by default for feign (BeanType beanName: ClassName): Decoder feignDecoder: ResponseEntityDecoder (which wraps a SpringDecoder) Encoder feignEncoder: SpringEncoder Logger feignLogger: Slf4jLogger Contract feignContract: SpringMvcContract Feign.Builder feignBuilder: HystrixFeign.Builder Client feignClient: if Ribbon is enabled it is a LoadBalancerFeignClient, otherwise the default feign client is used.
解决方法: 统一配置 Feign 的 Encoder 和 Decoder 的 Jackson 转换方式
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import feign.codec.Decoder;
import feign.codec.Encoder;
@Configuration
public class CustomFeignConfig {
@Bean
public Decoder feignDecoder() {
HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter(customObjectMapper());
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(jacksonConverter);
return new ResponseEntityDecoder(new SpringDecoder(objectFactory));
}
@Bean
public Encoder feignEncoder(){
HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter(customObjectMapper());
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(jacksonConverter);
return new SpringEncoder(objectFactory);
}
public ObjectMapper customObjectMapper(){
ObjectMapper objectMapper = new ObjectMapper();
//Customize as much as you want
objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
return objectMapper;
}
}如果配置 DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT 不起作用可以试试这种方式
public ObjectMapper customObjectMapper(){
ObjectMapper objectMapper = new ObjectMapper();
//Customize as much as you want
objectMapper.registerModule(new StringSanitizerModule());
return objectMapper;
}
public class StringSanitizerModule extends SimpleModule {
public StringSanitizerModule() {
addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
return StringUtils.trimToNull(jsonParser.getValueAsString());
}
});
}
}