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

 2019-11-23 11:09  阅读(1701)
文章分类:Spring Cloud

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);

来源:http://ddrv.cn/a/88268

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> spring cloud在项目中的应用 --- Feign

相关推荐