首页 > 其他分享 >Feign 动态设定服务器名称 与 调用接口

Feign 动态设定服务器名称 与 调用接口

时间:2024-07-15 09:55:33浏览次数:17  
标签:Feign java import 接口 public private new 服务器 HttpHeaders

 

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,java,import,接口,public,private,new,服务器,HttpHeaders
From: https://www.cnblogs.com/uoky/p/18302515

相关文章

  • 阿里云搭建vps服务器的过程
    最近突发奇想想要搭建一个阿里云的的vps服务器,下面是搭建的过程:首先,登录阿里云网站:搜索,esc控制台:点击创建实例:选择地区:选择实例规格:选择镜像:将存储规格改成100G(40G可能不够用)点击下一步:这里面不需要更改,点击下一步设置连接密码即可创建成功后点击启动......
  • Python监控服务器状态程序
    前言最近服务器有几次被安全狗断网,没找到好的解决方法。就先写了个Python程序监控网络状态,断网5分钟强制重启服务器的脚步。代码importpsutilimportsocketimporttimeimportosimportdatetimedefcheck_internet_connection():try:socket.creat......
  • bluez --- 接口说明
    蓝牙bluez对应的服务是org.bluez org.bluez.Adapter1服务:org.bluez对象:/org/bluez/hci0接口:org.bluez.Adapter1文档bluez5_SDK/doc/adapter-api.txt功能启动扫描蓝牙设备(方法:StartDiscovery)org.freedesktop.DBus.ObjectManager服务:org.bluez对象:/接口:org.freed......
  • ubuntu中nginx部署服务器后添加SSL证书解决SSL handshake failed问题
    文章思路:写这篇文章主要是本人在使用nginx部署好服务器后,采用浏览器打开服务器地址;在采用pyqt开发的软件中,采用QNetwork模块连接后,想下载服务器的zip压缩包时,发现QNetwork提示报了SSLhandshakefailed异常问题解决方式:添加SSL证书进行解决,目前要获取根证书有以下方式;(1)从证......
  • OpenFeign 使用细节
    @FeignClient注解配置项public@interfaceFeignClient{//和value互为别名,标示要调用哪个服务,要和nacos上面的服务名一致@AliasFor("name")Stringvalue()default"";//当服务提供者的接口太多了,调用方想分为多个FeignClient,就要指定不同的......
  • 推荐几个开源HTTP服务接口生成工具
    在现在流行微服务、前后端分离软件开发架构下,基于标准RESTful/JSON的HTTP接口已经成为主流。在实际业务中有很多需要快速开发调用数据服务接口的需求,但团队中缺乏专业的后端开发人员,比如:(1)数据库表已存在,如何快速把数据变成服务,供报表、大屏等数据可视化方式使用。(2)移动APP/H5/小......
  • OpenFeign 服务调用与负载
    需要建两个工程,一个是服务提供者,一个是服务调用者服务提供者一个普通的nacos服务,增加一个controller方法即可,上一篇文章刚说了,这里简单说下<!--服务提供者不调用其他服务,所以只需要注册到nacos的依赖--><dependency><groupId>com.alibaba.cloud</groupId><a......
  • 讯飞星火最新V4.0 AI模型接口分享
     讯飞星火认知大模型是科大讯飞发布的大模型。该模型具有7大核心能力,即文本生成、语言理解、知识问答、逻辑推理、数学能力、代码能力、多模交互,该模型对标ChatGPT。就在上个月6.27号讯飞也是发布了星火4.0版本,星火官方宣称4.0版本全面对标GPT-4Turbo。兼容openai接口的......
  • python接口自动化(二十五)--unittest断言——下(详解)
    1.简介 本篇还是回归到我们最初始的话题,想必大家都忘记了,没关系看这里:传送门 没错最初的话题就是登录,由于博客园的登录机制改变了,本篇以我找到的开源免费的登录API为案例,结合unittest框架写2个用例。同样我们先来看一下接口文档。2.接口文档2.1登录接口请求方式......
  • 去水印小程序源码修复版-前端后端内置接口+第三方接口
    内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍去水印小程序源码,前端+后端,内置接口+第三方接口,修复数据库账号密码错误问题,内置接口支持替换第三方接口,文件挺全的,可以添加流量主代码,搭建需要准备一台服务器,备案域名和http......