spring cloud在项目中的应用 --- Feign

Feign

返回值解析

在我们项目中,微服务的所有返回参数都规定了格式,webResponse.java:

{
  "success": true,
  "message": "成功",
  "code": 20131003,
  "data": {} }

所以,对于从微服务而来的返回值,可以统一去做处理。在项目中规定所有从微服务而来的返回都带返回头
“X-Service-Response”,从而可以对带有这个响应头的返回进行特殊处理。

public class ServiceResponseDecoder implements Decoder {

    private Decoder decoder;

    public ServiceResponseDecoder(Decoder decoder) {
        this.decoder = decoder;
    }

    static final List<String> JSON_TYPE_HEADERS = Lists.newArrayList(MediaType.APPLICATION_JSON_UTF8_VALUE
            , MediaType.APPLICATION_JSON_VALUE, APPLICATION_JSON_UTF8_VALUE);

    @Override
    public Object decode(final Response response, Type type) throws IOException, FeignException {
        if (!isJsonResponse(response) || !isServiceResponse(response)) {
            return decoder.decode(response, type);
        }
        WebReponse serviceResponse = decoder.decode(response, WebReponse.class);
        if (!serviceResponse.isSuccess()) {
            throw new BaseException(serviceResponse.getCode(),serviceResponse.getMessage());
        }
        return JsonUtil.toBean(serviceResponse.getData(), ((Class) type));
    }

    private boolean isServiceResponse(Response response) {
        return !ObjectUtils.isEmpty(response.headers().get("X-Service-Response"));
    }

    private boolean isJsonResponse(Response response) {
        return JSON_TYPE_HEADERS.stream()
                .anyMatch(
                        jsonType -> jsonType.equals(
                                Optional.of(response.headers().get(HttpHeaders.CONTENT_TYPE))
                                        .orElseGet(() -> Lists.newArrayList())
                                        .stream()
                                        .findFirst()
                                        .get()
                        )
                );
    }
}

错误日志

feign的ErrorDecoder接口中,有个Default类,是feign默认的errorDecode方式。处理的范围是响应码不在2xx范围内的。在这里,虽然error被解析了,但是被拼成了一个字符串传给了应用层。对于我们的项目开发来说,调用接口出错时,必然需要打印日志,所以可以实现ErrorDecoder接口,完成异常日志打印的功能。

请求拦截

由于项目需求,我们有个微服务,起了多个实例,每个实例以服务名+UUID的方式注册在Eureka中,而且可能会变更。但FeignClient的加载是在程序启动时加载的,如果每个实例都配置FeignClient,既达不到动态的要求,也使得代码非常冗余。而使用Feign.builder()去创建个客户端又和整体代码风格完全迥异。阅读FeignClient的文档后,发现提供请求参数拦截的功能,于是可以使用此功能完成要求。

继承LoadBalancerFeignClient,实现其方法,代码如下所示。可以看到,在url的请求中,将FeignClient生成的http://clientName/path转为了http://mc-client-name-uuid/path的方式。由于拦截替换是在ribbon处理之前,所以,ribbon在截取clientName时,会使用mc-client-name-uuid来替换之前额度clientName。当然,除此之外,请求参数、请求头等均可在此处理。

@Override
    public Response execute(Request request, Request.Options options) throws IOException {
        URI asUri = URI.create(request.url());
        String clientName = asUri.getHost();
        StringBuffer newURL = new StringBuffer(10).append(asUri.getScheme())
                .append("://")
                .append(MC_CLIENT_NAME)
                .append("-")
                .append(request.headers().get(MC_ID).stream().findFirst().get())
                .append("/")
                .append(asUri.getPath());
        if (StringUtils.hasLength(asUri.getQuery())){
            newURL.append("?").append(asUri.getQuery());
        }
        return super.execute(Request.create(request.method(), newURL.toString(), request.headers(), request.body(), request.charset()), options);
    }

文件上传

原生的FeignClient不支持文件上传,但FeignClient是基于OpenFeign做的集成,而OpenFeign是支持文件上传的,那么就可以扩展FeignClient的Configuration,使其可以支持文件上传。
引入OpenFeign的两个依赖

    compile "io.github.openfeign.form:feign-form:3.0.0"
    compile "io.github.openfeign:feign-jackson:9.5.1"

继承FeignClient的配置文件,替换默认的对象配置。

class FormDataFeignConfiguration extends FeignClientsConfiguration {

        @Autowired
        private ObjectFactory<HttpMessageConverters> messageConverters;

        @Bean
        @Primary
        Encoder feignFormEncoder() {
            return new FormEncoder(new SpringEncoder(this.messageConverters));
        }

        @Override
        public Encoder feignEncoder() {
            return new FormEncoder(new JacksonEncoder());
        }


        @Override
        public Decoder feignDecoder() {
            //解析文件服务器返回的参数
            return new FileResponseDecoder(super.feignDecoder());
        }

        @Primary
        @Bean
        public Contract useFeignAnnotations() {
            return new Contract.Default();
        }
    }

上传文件FeignClient接口为:

@RequestLine("POST /v1/file/upload")
    @Headers("Content-Type: multipart/form-data")
    JsonNode upload(@Param("file") File file, @Param("name") String fileName, @Param("type") String fileType);
    原文作者:Spring Cloud
    原文地址: https://blog.csdn.net/mujushi/article/details/79792338
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞