OpenFeign uses FastJson to parse the returned data

In a microservice project, one service needs to call another service to obtain relevant data. Feign components can be easily called, and related dependencies and annotations can be added to quickly use it.

In practical applications, the returned data needs to be parsed after the call. At this time, there is a problem. If it is multi-layer encapsulated data

, how to get it? The first thing that comes to mind is the direct transfer. If you simply return the object in this way, there is no problem, but the required data is the collection in the object, and the collection obtained by direct transfer cannot be directly operated. At this time, you must not Not another way. Feign calls can be imagined as http calls, and the data format returned by http is json, so can json be used to parse the data returned by feign? If it is possible, we can directly use FastJson to convert it into the collection we need and then operate it. However, there is a problem in the parsing process using FastJson. Debug finds that the returned data is not json data at all. FastJson directly throws a parsing exception. Where is the problem? To solve the problem, the returned data is not json, so let it return json data, can't you continue to operate? So how to make feign return the json data we need! ! !

After checking the relevant information, I found that (looking for Du Niang), because Feign does not share the message converter chain of Spring MVC, the Jackson Json parsing library is used by default. As long as we replace both the server and the client with FastJson, we can theoretically return json data.

So let's configure

Server-side configuration, that is, the party that provides the service

@Configuration
public class FastJsonConverterConfig extends WebMvcConfigurationSupport {

    /**
     * 使用fastjson代替jackson
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        //先把JackSon的消息转换器删除.
        //(1)源码分析可知,返回json的过程为:
        //  Controller调用结束后返回一个数据对象,for循环遍历conventers,找到支持application/json的HttpMessageConverter,然后将返回的数据序列化成json。
        //  具体参考org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor的writeWithMessageConverters方法
        //(2)由于是list结构,我们添加的fastjson在最后。因此必须要将jackson的转换器删除,不然会先匹配上jackson,导致没使用fastjson
        for (int i = converters.size() - 1; i >= 0; i--) {
            if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
                converters.remove(i);
            }
        }

        //自定义fastjson配置
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(
                // 是否输出值为null的字段,默认为false,我们将它打开
                SerializerFeature.WriteMapNullValue,
                // 将Collection类型字段的字段空值输出为[]
                SerializerFeature.WriteNullListAsEmpty,
                // 将字符串类型字段的空值输出为空字符串
                SerializerFeature.WriteNullStringAsEmpty,
                // 将数值类型字段的空值输出为0
                SerializerFeature.WriteNullNumberAsZero,
                SerializerFeature.WriteDateUseDateFormat,
                // 禁用循环引用
                SerializerFeature.DisableCircularReferenceDetect
        );

        // 添加支持的MediaTypes;不添加时默认为*/*,也就是默认支持全部
        // 但是MappingJackson2HttpMessageConverter里面支持的MediaTypes为application/json
        List<MediaType> fastMediaTypes = new ArrayList<>();
        fastMediaTypes.add(MediaType.APPLICATION_JSON);
        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);

        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        fastJsonHttpMessageConverter.setFastJsonConfig(config);
        fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
        converters.add(fastJsonHttpMessageConverter);
        //支持XML格式的请求
        converters.add(new StringHttpMessageConverter());
    }

    /**
     * 发现如果继承了WebMvcConfigurationSupport,则在yml中配置的相关内容会失效。 需要重新指定静态资源
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations(
                "classpath:/static/");
        registry.addResourceHandler("swagger-ui.html").addResourceLocations(
                "classpath:/META-INF/resources/");
        registry.addResourceHandler("doc.html").addResourceLocations(
                "classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations(
                "classpath:/META-INF/resources/webjars/");
        super.addResourceHandlers(registry);
    }

}

客户端配置,也就是调用的一方

@Configuration
public class FeignSimpleEncoderConfig {
    @Bean
    Logger.Level feignLoggerLevel() {
        //这里记录所有,根据实际情况选择合适的日志level
        return Logger.Level.FULL;
    }

    @Bean
    public Encoder feignEncoder() {
        return new SpringEncoder(feignHttpMessageConverter());
    }

    @Bean
    public Decoder feignDecoder() {
        return new SpringDecoder(feignHttpMessageConverter());
    }

    /**
     * 设置解码器为fastjson
     *
     * @return
     */
    private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
        final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
        return () -> httpMessageConverters;
    }

    private FastJsonHttpMessageConverter getFastJsonConverter() {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();

        List<MediaType> supportedMediaTypes = new ArrayList<>();
        MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_UTF8_VALUE);
        supportedMediaTypes.add(mediaTypeJson);
        converter.setSupportedMediaTypes(supportedMediaTypes);
        FastJsonConfig config = new FastJsonConfig();
        config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
        config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
        converter.setFastJsonConfig(config);

        return converter;
    }
}

Feign calls class-specified configuration

//远程调用
@FeignClient(value = "nakadai",fallback = NakadaiClientImpl.class,configuration = FeignSimpleEncoderConfig.class)
@Component
@Headers("Accept: application/json")
public interface NakadaiClient {

    /**
     * 查询数据
     */
    @Headers({"Content-Type: application/json","Accept: application/json"})
    @GetMapping(value = "/nakadai/nakadai/order/getCustomerOrder",consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    R getCustomerOrder(@RequestParam("id") Integer id);

}

Restart again, debug can find that the returned data conforms to the json format, and then you can operate on the json data

example:

R result= nakadaiClient.getCustomerOrder(id);
Object orderList = customerOrder.get("orderList");
String jsonString = JSON.toJSONString(orderList);
List<Order> orders = JSON.parseArray(jsonString, Order.class);

Although such conversion is more troublesome, there is no problem caused by direct forced conversion when operating the collection

In actual use, another problem was discovered. After the server configuration was completed, it was found that the return format was all garbled, which made it impossible to correctly obtain the data returned by the interface. Then, the configuration class was deleted, and the data returned by feign was still in json format. This shows that The server does not need to be configured, but there will be problems with the configuration. After finding the garbled characters, just delete the configuration class directly.

Guess you like

Origin blog.csdn.net/m0_46803792/article/details/119147166