1. 新增编码器(由于使用了动态的Feign,所以不能像正常使用Feign一样指定configuration配置编码器)

 import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry; /**
* A custom {@link Encoder} that supports Multipart requests. It uses
* {@link HttpMessageConverter}s like {@link RestTemplate} does.
*
* @author Pierantonio Cangianiello
*/
public class FeignSpringFormEncoder implements Encoder { private final List<HttpMessageConverter<?>> converters = new RestTemplate().getMessageConverters(); public static final Charset UTF_8 = Charset.forName("UTF-8"); public FeignSpringFormEncoder() {
} /**
* {@inheritDoc }
*/
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
if (isFormRequest(bodyType)) {
final HttpHeaders multipartHeaders = new HttpHeaders();
multipartHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
encodeMultipartFormRequest((Map<String, ?>) object, multipartHeaders, template);
} else {
final HttpHeaders jsonHeaders = new HttpHeaders();
jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
encodeRequest(object, jsonHeaders, template);
}
} /**
* Encodes the request as a multipart form. It can detect a single {@link MultipartFile}, an
* array of {@link MultipartFile}s, or POJOs (that are converted to JSON).
*
* @param formMap
* @param template
* @throws EncodeException
*/
private void encodeMultipartFormRequest(Map<String, ?> formMap, HttpHeaders multipartHeaders, RequestTemplate template) throws EncodeException {
if (formMap == null) {
throw new EncodeException("Cannot encode request with null form.");
}
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
for (Entry<String, ?> entry : formMap.entrySet()) {
Object value = entry.getValue();
if (isMultipartFile(value)) {
map.add(entry.getKey(), encodeMultipartFile((MultipartFile) value));
} else if (isMultipartFileArray(value)) {
encodeMultipartFiles(map, entry.getKey(), Arrays.asList((MultipartFile[]) value));
} else {
map.add(entry.getKey(), encodeJsonObject(value));
}
}
encodeRequest(map, multipartHeaders, template);
} private boolean isMultipartFile(Object object) {
return object instanceof MultipartFile;
} private boolean isMultipartFileArray(Object o) {
return o != null && o.getClass().isArray() && MultipartFile.class.isAssignableFrom(o.getClass().getComponentType());
} /**
* Wraps a single {@link MultipartFile} into a {@link HttpEntity} and sets the
* {@code Content-type} header to {@code application/octet-stream}
*
* @param file
* @return
*/
private HttpEntity<?> encodeMultipartFile(MultipartFile file) {
HttpHeaders filePartHeaders = new HttpHeaders();
filePartHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
try {
Resource multipartFileResource = new MultipartFileResource(file.getOriginalFilename(), file.getSize(), file.getInputStream());
return new HttpEntity<>(multipartFileResource, filePartHeaders);
} catch (IOException ex) {
throw new EncodeException("Cannot encode request.", ex);
}
} /**
* Fills the request map with {@link HttpEntity}s containing the given {@link MultipartFile}s.
* Sets the {@code Content-type} header to {@code application/octet-stream} for each file.
*
* @param map current request map.
* @param name the name of the array field in the multipart form.
* @param files
*/
private void encodeMultipartFiles(LinkedMultiValueMap<String, Object> map, String name, List<? extends MultipartFile> files) {
HttpHeaders filePartHeaders = new HttpHeaders();
filePartHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
try {
for (MultipartFile file : files) {
Resource multipartFileResource = new MultipartFileResource(file.getOriginalFilename(), file.getSize(), file.getInputStream());
map.add(name, new HttpEntity<>(multipartFileResource, filePartHeaders));
}
} catch (IOException ex) {
throw new EncodeException("Cannot encode request.", ex);
}
} /**
* Wraps an object into a {@link HttpEntity} and sets the {@code Content-type} header to
* {@code application/json}
*
* @param o
* @return
*/
private HttpEntity<?> encodeJsonObject(Object o) {
HttpHeaders jsonPartHeaders = new HttpHeaders();
jsonPartHeaders.setContentType(MediaType.APPLICATION_JSON);
return new HttpEntity<>(o, jsonPartHeaders);
} /**
* Calls the conversion chain actually used by
* {@link RestTemplate}, filling the body of the request
* template.
*
* @param value
* @param requestHeaders
* @param template
* @throws EncodeException
*/
private void encodeRequest(Object value, HttpHeaders requestHeaders, RequestTemplate template) throws EncodeException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
HttpOutputMessage dummyRequest = new HttpOutputMessageImpl(outputStream, requestHeaders);
try {
Class<?> requestType = value.getClass();
MediaType requestContentType = requestHeaders.getContentType();
for (HttpMessageConverter<?> messageConverter : converters) {
if (messageConverter.canWrite(requestType, requestContentType)) {
((HttpMessageConverter<Object>) messageConverter).write(
value, requestContentType, dummyRequest);
break;
}
}
} catch (IOException ex) {
throw new EncodeException("Cannot encode request.", ex);
}
HttpHeaders headers = dummyRequest.getHeaders();
if (headers != null) {
for (Entry<String, List<String>> entry : headers.entrySet()) {
template.header(entry.getKey(), entry.getValue());
}
}
/*
we should use a template output stream... this will cause issues if files are too big,
since the whole request will be in memory.
*/
template.body(outputStream.toByteArray(), UTF_8);
} /**
* Minimal implementation of {@link HttpOutputMessage}. It's needed to
* provide the request body output stream to
* {@link HttpMessageConverter}s
*/
private class HttpOutputMessageImpl implements HttpOutputMessage { private final OutputStream body;
private final HttpHeaders headers; public HttpOutputMessageImpl(OutputStream body, HttpHeaders headers) {
this.body = body;
this.headers = headers;
} @Override
public OutputStream getBody() throws IOException {
return body;
} @Override
public HttpHeaders getHeaders() {
return headers;
} } /**
* Heuristic check for multipart requests.
*
* @param type
* @return
* @see feign.Types
*/
static boolean isFormRequest(Type type) {
return MAP_STRING_WILDCARD.equals(type);
} /**
* Dummy resource class. Wraps file content and its original name.
*/
static class MultipartFileResource extends InputStreamResource { private final String filename;
private final long size; public MultipartFileResource(String filename, long size, InputStream inputStream) {
super(inputStream);
this.size = size;
this.filename = filename;
} @Override
public String getFilename() {
return this.filename;
} @Override
public InputStream getInputStream() throws IOException, IllegalStateException {
return super.getInputStream(); //To change body of generated methods, choose Tools | Templates.
} @Override
public long contentLength() throws IOException {
return size;
} } }

2. 配置文件中配置编码器

feign.client.config.default.encoder=完整包名.FeignSpringFormEncoder

  

3. 新增FeignClientUtil类

import org.springframework.beans.BeansException;
import org.springframework.cloud.openfeign.FeignClientBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; @Component
public class FeignClientUtils implements ApplicationContextAware { private static ApplicationContext applicationContext = null;
private static final Map<String, Object> BEAN_CACHE = new ConcurrentHashMap<>(); @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (FeignClientUtils.applicationContext == null) {
FeignClientUtils.applicationContext = applicationContext;
}
} public static <T> T build(String serverName, Class<T> targetClass) {
return buildClient(serverName, targetClass);
} @SuppressWarnings("unchecked")
private static <T> T buildClient(String serverName, Class<T> targetClass) {
T t = (T) BEAN_CACHE.get(serverName);
if (Objects.isNull(t)) {
FeignClientBuilder.Builder<T> builder = new FeignClientBuilder(applicationContext).forType(targetClass, serverName); t = builder.build();
BEAN_CACHE.put(serverName, t);
}
return t;
}
}

  

4. 新增interface

import xxx.ApiResult;
import org.springframework.web.bind.annotation.*; import java.util.Map; public interface IFeignClientTemplate { @GetMapping(value = "/{url}", headers = {"Content-Type: application/json"})
ApiResult get(@PathVariable("url") String url, @RequestParam Map<String, Object> params); @PostMapping(value = "/{url}", headers = {"Content-Type: application/json"}, consumes = "application/json")
ApiResult post(@PathVariable("url") String url, Map<String, Object> params); @PostMapping(value = "/{url}")
ApiResult postFile(@PathVariable("url") String url, Map<String, ?> params);
}

  

5. 使用实例

String serviceName = "xxx"; // 注册在nacos的服务名称
String url = "xxx"; // 请求的url的接口
DynamicFeignClient client = FeignClientUtils.build(serviceName, DynamicFeignClient.class);
ApiResult result = client.get(url,new HashMap<>());

  

PS:过程中遇到了一些问题,也使用了RequestLine注解,最后都调整与解决了,编码器是必要的,不然无法转换为指定的DTO

如果有人知道怎么在这种情况下使用Configuration配置编码解码注解,也请不吝赐教(试了几个都不行,在build client中指定也不行,没有对应方法)

target values must be absolute.

java.lang.IllegalStateException: Method has too many Body parameters: public abstract java.lang.String xxx.IFeignClientTemplate.get(java.lang.String,java.lang.Object)

feign.FeignException$NotFound: status 404 reading IFeignClientTemplate#get(String)

status 405 reading IFeignClientTemplate#get(String,HashMap)

No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)

等报错

  

Feign 动态设定服务器名称 与 调用接口的更多相关文章

  1. C#动态webservice调用接口 (JAVA,C#)

    C#动态webservice调用接口 using System; using System.Collections; using System.IO; using System.Net; using ...

  2. C#调用接口注意要点 socket,模拟服务器、客户端通信 在ASP.NET Core中构建路由的5种方法

    C#调用接口注意要点   在用C#调用接口的时候,遇到需要通过调用登录接口才能调用其他的接口,因为在其他的接口需要在登录的状态下保存Cookie值才能有权限调用, 所以首先需要通过调用登录接口来保存c ...

  3. SpringCloud:Feign调用接口不稳定问题以及如何设置超时

    1. Feign调用接口不稳定报错 Caused by: java.net.SocketException: Software caused connection abort: recv failed ...

  4. Spring Cloud Feign 在调用接口类上,配置熔断 fallback后,输出异常

    Spring Cloud Feign 在调用接口类上,配置熔断 fallback后,出现请求异常时,会进入熔断处理,但是不会抛出异常信息. 经过以下配置,可以抛出异常: 将原有ErrorEncoder ...

  5. python网络-动态Web服务器案例(30)

    一.浏览器请求HTML页面的过程 了解了HTTP协议和HTML文档,其实就明白了一个Web应用的本质就是: 浏览器发送一个HTTP请求: 服务器收到请求,生成一个HTML文档: 服务器把HTML文档作 ...

  6. Zookeeper动态更新服务器列表

    -------------------------------------------------------------------------------------- [版权申明:本文系作者原创 ...

  7. 【一起学源码-微服务】Feign 源码二:Feign动态代理构造过程

    前言 前情回顾 上一讲主要看了@EnableFeignClients中的registerBeanDefinitions()方法,这里面主要是 将EnableFeignClients注解对应的配置属性注 ...

  8. URLConnection调用接口

    写在前面: 项目是java web,jdk1.4,weblogic 7;对方.net系统,用wcf开发的接口.对方提供接口url地址,以及说明用post方式去调用,无需传递参数,直接返回json ar ...

  9. SpringCloud03 Ribbon知识点、 Feign知识点、利用RestTemplate+Ribbon调用远程服务提供的资源、利用feign调用远程服务提供的资源、熔断

    1 远程服务资源的调用 1.1 古老的套路 在微服务出现之前,所有的远程服务资源必须通过RestTemplate或者HttpClient进行:但是这两者仅仅实现了远程服务资源的调用,并未提供负载均衡实 ...

  10. Spring Cloud 之 Feign 知识点:封装了 REST 调用

    Feign Client 会在底层根据你的注解,跟你指定的服务建立连接.构造请求.发起请求.获取响应.解析响应,等等. Feign 的一个关键机制就是使用了动态代理. 首先,如果你对某个接口定义了 @ ...

随机推荐

  1. 历代iPad及Android平板的主要参数对比

    「程序员的备忘录系列」这笔记可是持续更新的哦     逻辑分辨率Point,也就是CSS像素,是进行网页适配的关键,以下是平时整理的一些备忘录数据,可以收藏. 以现在平板的销量,还没有手机的十分之一, ...

  2. webgl(threejs)生成房间楼层

    楔子 在很多数字孪生项目中,都会涉及到楼层的建模.楼层的建模由于结构繁多,如果都是建模师进行手动建模,工作量会比较大.而楼层本身的结构,可以抽象成可以通过路径构造的对象(这和之前的文章提及的的管路以及 ...

  3. docker卸载分享

    一.准备工作: 1.杀死docker有关的容器: docker kill $(docker ps -a -q) 2.删除所有docker容器: docker rm $(docker ps -a -q) ...

  4. my-http-server 静态服务器源码学习实现缓存及压缩

    目录 一.准备工作及流程说明 二.配置命令行 三.设置入口文件和渲染模板 四.my-http-server源码 一.准备工作及流程说明 一看这标题,大家可能一下子没有反应过来,到底是要干什么?那么就先 ...

  5. 学习笔记--初识Java面向对象

    面向对象与面向过程的区别 面向过程:主要关注具体过程,因果关系 优点:对于业务逻辑比较简单的程序,可以得到快速开发,前期投入成本比较低 缺点:由于面向过程的使用让程序间的元素"耦合度&quo ...

  6. layout文本相关

    Textview t=findViewById(R.id.t); ONE设置文本内容: 在XML中android:text直接写 在java中setText()中修改 注意点1继承appcompata ...

  7. 代码随想录Day1

    704.二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1. 示例 1: ...

  8. jpa+querydsl的平替国产easy-query最好用的orm

    jpa+querydsl的平替国产easy-query最好用的orm 一款国产最强java orm,完美支持可控强类型dsl,外加完美支持对象模型筛选拉取的orm,拥有非常智能的include(s)一 ...

  9. 【H5】06 网页架构

    摘自: https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Introduction_to_HTML/%E6%96%87%E4%BB%B6%E5%9 ...

  10. 【Spring Data JPA】07 Specifications动态查询

    [前言说明] 针对CRUD种的查询,因为我们的查询总是具有各种各样的筛选条件 为了我们的程序能够更加适应筛选条件的变化,SpringDataJpa提供了Specifications这种解决方案 Spe ...