总结一下openfeign的用法:

总结一下openfeign的用法:

分为三步:

1:引包导入

2:在启动类上加注解,启用feign. 可以扫包带有@FeignClient的,及可以设置它的配置类;

3:写feign接口,可以配置下feign接口的调用规则。

第一步引包,我这里用的是okhttp.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

第二步是在配置中加入注解启用。

@EnableFeignClients(basePackages = "com.gsafety.gemp.*.*.consumer.client", defaultConfiguration = {TokenFeignConfiguration.class})这里的TokenFeignConfiguration是一个配置类。

第三步写feign的配置类,

package com.macro.mall.config;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.macro.mall.common.api.CommonResult;
import com.macro.mall.common.exception.ApiException;
import com.qianda.mall.common.constant.Constants;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author xay
 * @version 1.0
 * @since 2020/11/5
 */
@Configuration
@RestControllerAdvice
public class FeignConfig {

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 通用feign响应值解析器
     */
    @Bean
    public Decoder feignCommonDecoder() {
        return (response, type) -> {
            Response.Body body = response.body();
            try (BufferedReader bufferedReader = new BufferedReader(body.asReader(Charset.forName("utf-8")));) {
                String bodyStr = bufferedReader.lines().collect(Collectors.joining());
                if (isDirectParse.test(type)) {
                    return objectMapper.readValue(bodyStr, TypeFactory.defaultInstance().constructType(type));
                }

                Type real;
                if (type instanceof ParameterizedType) {
                    real = ((ParameterizedType) type).getRawType();
                } else {
                    real = type;
                }
                JsonNode jsonNode = objectMapper.readTree(bodyStr);
                int code = jsonNode.get(Constants.RESULT_CODE).intValue();
                if (code != 200) {
                    throw new ApiException(jsonNode.get(Constants.RESULT_MSG).textValue());
                }
                String data = jsonNode.get(Constants.RESULT_DATA) == null ? "" : jsonNode.get(Constants.RESULT_DATA).toString();
                if (StringUtils.isBlank(data)) {
                    if (real == Optional.class) {
                        return Optional.empty();
                    } else {
                        return null;
                    }
                } else {
                    if (real == Optional.class) {
                        return Optional.of(objectMapper.readValue(data, TypeFactory.defaultInstance().constructType(((ParameterizedType) type).getActualTypeArguments()[0])));
                    } else {
                        return objectMapper.readValue(data, TypeFactory.defaultInstance().constructType(type));
                    }
                }
            }
        };
    }

    @Bean
    public Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }

    /**
     * 可以带上请求头
     * @return
     */
    @Bean
    RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                    .getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                Enumeration<String> headerNames = request.getHeaderNames();
                if (headerNames != null) {
                    while (headerNames.hasMoreElements()) {
                        String name = headerNames.nextElement();
                        if (exclude_headers.contains(name.toLowerCase())) {
                            continue;
                        }
                        String values = request.getHeader(name);
                        requestTemplate.header(name, values);
                    }
                }
            }
        };
    }

    private static final List<String> exclude_headers = Arrays.asList("content-type","content-length");
    private static Predicate<Type> isDirectParse = type -> type == CommonResult.class ||
            (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType() == CommonResult.class);


    /**
     * Feign 客户端的日志记录,默认级别为NONE
     * Logger.Level 的具体级别如下:
     * NONE:不记录任何信息
     * BASIC:仅记录请求方法、URL以及响应状态码和执行时间
     * HEADERS:除了记录 BASIC级别的信息外,还会记录请求和响应的头信息
     * FULL:记录所有请求与响应的明细,包括头信息、请求体、元数据
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    /**
     * Feign支持文件上传
     * @param messageConverters
     * @return
     */
    @Bean
    @Primary
    @Scope("prototype")
    public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }


    @ExceptionHandler(value = DecodeException.class)
    public CommonResult handleValidException(DecodeException e) {
        Throwable cause = e.getCause();
        if (cause != null && cause instanceof ApiException) {
            return CommonResult.failed(e.getMessage());
        }
        e.printStackTrace();
        return CommonResult.success("系统异常");
    }


}

配置类写完了,可以写具体的feignClient的接口了。

import com.gsafety.gemp.app.duty.consumer.config.FeignMultipartConfig;
import com.gsafety.gemp.app.duty.consumer.fallback.AppAttachmentClientHystrix;
import com.gsafety.gemp.common.result.Result;
import com.gsafety.gemp.common.util.AttachmentOutDTO;
//import com.gsafety.gemp.standard.basicFacilities.CustomJSONBody;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Map;

@Component
@FeignClient(value = "gemp-file", path = "/api/attachment", fallback = AppAttachmentClientHystrix.class,configuration = FeignMultipartConfig.class)
public interface AppAttachmentClient {

    /**
     * 附件上传
     *
     * @param file
     * @param module
     * @return
     */
    @PostMapping(value="/upload/v1",produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    AttachmentOutDTO upload(@RequestPart(value = "file") MultipartFile file,
                            @RequestParam(value = "module", required = false) String module);}  //这个FeignClient中fallback与configuration是非必填的。

我这里可以写一个可以实现FeignClient接口的fallback类,用来处理融断。

@Component("AppAttachmentClientHystrix")
public class AppAttachmentClientHystrix implements AppAttachmentClient {

    @Override
    public AttachmentOutDTO upload(MultipartFile file, String module) {
        throw new BusinessException("文件附件服务暂时不可用!");
    }}

如果你的@FeignClient接口中没有fallback属性就可以不用写上面的Hystrix了。

我的ObjectMapper也贴出来参考下:

@Bean
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
{
    ObjectMapper objectMapper = builder.createXmlMapper(false).build();
    // 全局配置序列化返回 JSON 处理
    SimpleModule simpleModule = new SimpleModule();
    //JSON Long ==> String
    simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
    simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(local_datetime_formatter));
    simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(local_datetime_formatter));
    simpleModule.addSerializer(BaseEnum.class, new JsonSerializer<BaseEnum>() {
        @Override
        public void serialize(BaseEnum value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            gen.writeObject(value.getValue());
        }
    });
    simpleModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
    /*
    * TODO 类型条件限定过宽且仅做了名称处理
    * */
    simpleModule.addDeserializer(Enum.class, new CommonDeserializer());
    objectMapper.registerModule(simpleModule);
    return objectMapper;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值