feign form支持

fegin使用psot调用application/x-www-form-urlencoded类型请求传参414,413参数缺失错误等问题 思路如下:重写拦截器,获取请求是post并且是application/x-www-form-urlencoded的请求,判断一下body是否有值,没有值的情况下去emplate.queries里面取@RequestParam的参数值,将url的值重新赋值给body,手动改造请求体,再将emplate.queries滞空。1、服务器修改接收参数的大小,一般调整nginx或者网关类的前置配置,但是调大后会影响服务器性能,一般情况对方也不会理会这种解决方式。看一下是不是两次调用时body的大小是不一样的。 阅读详情

feign是一个非常好用的http客户端工具,简单入门请见上篇文章,不多做介绍
但是在使用feign的时候也碰到了一点小坑,今天就来讲讲怎么解决这个坑

feign bean提交

看官方文档,feign post提交的时候可以使用bean传输,不需要每个参数注解@Param,然而feign会把这个bean的内容写入到http的 body中去。contentType为applicationJson
spring mvc接收需要在接口对应的bean上注解@RequestBody,从body中读取这个bean的内容。
如下

@Headers("Content-Type: application/json")
@RequestLine("POST /test1")
public BaseResponse test1(Demo demo);
@ResponseBody
@RequestMapping(value = "/test1", method = RequestMethod.POST)
public BaseResponse test1(@RequestBody Demo demo) {
    return BaseResponse.SUCCESS_RESPONSE;
}

使用一直很舒畅,因为自己的项目都是restful风格的接口
直到调用公司其他的项目组接口发现完蛋了,对方不是用body接收复杂对象

临时方案

为了解决这种问题只能使用@Param了,但是参数多的时候会写很长的方法参数

@Headers("Content-Type: application/x-www-form-urlencoded")
@RequestLine("POST /test2")
public BaseResponse test2(@Param("id") int id,@Param("name")String name);
@ResponseBody
@RequestMapping(value = "/test2", method = RequestMethod.POST)
public BaseResponse test2(Demo demo) {
   return BaseResponse.SUCCESS_RESPONSE;
}

另一种方式就是使用@QueryMap了

@Headers("Content-Type: application/x-www-form-urlencoded")
@RequestLine("POST /test2")
public BaseResponse test3(@QueryMap Map<String,Object> param);

这样暂时是对付过去了,但是总归不是很优雅。

扩展feign

仔细研究feign的源码后,发现feign是根据目标接口生成代理对象,生成代理对象的过程中会根据接口方法生成一个MethodMetadata对象,其中封装了方法签名configKey,form表单参数列表formParams,参数index对应的参数名indexToName等。属性如下:

  private String configKey;
  private transient Type returnType;
  private Integer urlIndex;
  private Integer bodyIndex;
  private Integer headerMapIndex;
  private Integer queryMapIndex;
  private boolean queryMapEncoded;
  private transient Type bodyType;
  private RequestTemplate template = new RequestTemplate();
  private List<String> formParams = new ArrayList<String>();
  private Map<Integer, Collection<String>> indexToName =
      new LinkedHashMap<Integer, Collection<String>>();
  private Map<Integer, Class<? extends Expander>> indexToExpanderClass =
      new LinkedHashMap<Integer, Class<? extends Expander>>();
  private Map<Integer, Boolean> indexToEncoded = new LinkedHashMap<Integer, Boolean>();
  private transient Map<Integer, Expander> indexToExpander;

而生成这个对象的类是Contract,可以在Feign构造器中设置。
可以自己扩展Contract,将复杂对象的参数名设置进indexToName就行了,这里虽然是int->集合的类型。但是在调用我们远程接口时,feign会将我们的参数转化为param->value的map形式。而feign在转换的过程中,如果indexToName index对应的name有多个的话,会迭代这个collection,然后讲传入的参数设置进去,并不会解析其中的属性,如下:

   @Override
    public RequestTemplate create(Object[] argv) {
      RequestTemplate mutable = new RequestTemplate(metadata.template());
      ...
      Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
      for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
        int i = entry.getKey();
        Object value = argv[entry.getKey()];
        if (value != null) { // Null values are skipped.
          ...
          for (String name : entry.getValue()) {
            varBuilder.put(name, value);
          }
        }
      }
      ...
      return template;
    }

以class Demo{ int id,String name} 为例,如果indexToName为 0->[“id”,”name”],最后解析出来就是
{“id”:“{id:1,name:chen}”,“name”“{id:1,name:chen}”};根本不是我们想要的结果
最后灵机一动,直接将参数名设为一个固定字符串就行,反正转换调用的参数时可以获得属性名
方案如下:
1.将带有@FormBean标注的参数的参数名定义为@FORM@+index,indexToName中为index->”@FORM@index”
“@FORM@”为自定义的一个特殊字符,怕冲突可以使用class的hashcode
2.调用时,在encoder中将参数名为”@FORM@”开头的参数删除,将传入的参数转换为map,添加到参数键值对中

public class FormContract extends Contract.Default {

    public static  String FORM_PARAM_NAME="@FORM@";

    private String formParamName;

    public FormContract() {
        this(FORM_PARAM_NAME);
    }

    public FormContract(String formParamName) {
        this.formParamName = formParamName;
    }

    @Override
    protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
        boolean isHttpAnnotation = super.processAnnotationsOnParameter(data, annotations, paramIndex);
        for (Annotation annotation : annotations) {
            Class<? extends Annotation> annotationType = annotation.annotationType();
            if (annotationType == FormBean.class) {
                FormBean paramAnnotation = (FormBean) annotation;
                //注解了FormBean 的参数名定义为@FORM@+index
                String name=formParamName+paramIndex;
                nameParam(data, name, paramIndex);
                Class<? extends Param.Expander> expander = paramAnnotation.expander();
                if (expander != Param.ToStringExpander.class) {
                    data.indexToExpanderClass().put(paramIndex, expander);
                }
                data.indexToEncoded().put(paramIndex, paramAnnotation.encoded());
                isHttpAnnotation = true;
                String varName = '{' + name + '}';
                if (!data.template().url().contains(varName) &&
                        !searchMapValuesContainsSubstring(data.template().queries(), varName) &&
                        !searchMapValuesContainsSubstring(data.template().headers(), varName)) {
                    data.formParams().add(name);
                }
            }            
        }
        return isHttpAnnotation;
    }

    private static <K, V> boolean searchMapValuesContainsSubstring(Map<K, Collection<String>> map,String search) {
    ......
    }
}
@Override
    public void encode(Object object, Type bodyType, RequestTemplate template) {
        ......
        @SuppressWarnings("unchecked")
        Map<String, Object> data = (Map<String, Object>) object;
        formObjectExpand(data);
        processors.get(formType).process(data, template);
    }
    private void formObjectExpand(Map<String, Object> data) {
        List<String> readyRemove=new ArrayList<>();
        Map<String,Object> insert=new HashMap<>();
        for(String key:data.keySet()){
            if(key.startsWith(FORM_PARAM_NAME)){
                Object value = data.get(key);
                readyRemove.add(key);
                try {
                    insert.putAll(objectConvertor.toMap(value));
                } catch (ConvertException e) {
                    LoggerUtil.logException(e);
                }
            }
        }
        //ConcurrentModificationException
        data.putAll(insert);
        for (String key : readyRemove) {
            data.remove(key);
        }
    }

具体例子请见我的github代码 FormContractFormEncoder
生成feign客户端的方式请见FeinFactory

调用代码变为

    @Headers("Content-Type: application/x-www-form-urlencoded")
    @RequestLine("POST /test2")
    public BaseResponse test4(@FormBean Demo demo);

第一次扩展没中文文档的开源框架,英语不好,水平有限,理解有问题的地方请不吝指出。

open-feign调用接口写法总结 阅读详情

相关推荐

EMD (经验模态分解) 在 C++ 中的实现指南

EMD 是由 Norden E. Huang 等人在1998年提出的一种适用于非线性和非平稳信号的分析方法。对称性:IMF 的上下包络对称。零交叉点:IMF 的零交叉点数量与极值点数量相同或相差一个。本文详细介绍了如何在C++中实现EMD,包括理论背景、算法步骤、代码实现和优化等内容。通过丰富的代码示例和详细的解释,希望能够帮助读者掌握EMD的基本原理,并能够在实际项目中灵活应用。未来,随着信号处理技术的不断发展,EMD及其变种(如EEMD、CEEMDAN等)将继续在各个领域发挥重要作用。

qq_38334677的博客 1711

feign发起POST请求application/x-www-form-urlencoded

关于feign发起post请求且参数类型为application/x-www-form-urlencoded

weixin_45614626的博客 5355

【Bluedroid】AVRCP 连接源码分析(一)

AVRCP连接源码分析流程是一个涉及多个步骤和协议交互的复杂过程。在源码中,需要仔细处理各种连接状态和协议交互,以确保AVRCP连接的稳定性和可靠性。同时,还需要根据具体的应用场景和需求,对AVRCP命令和响应进行定制化处理。

byte轻骑兵的技术小窝 3296

Feign支持post方法的表单提交

摘要:本文介绍了如何配置Feign支持POST方法的表单提交。默认情况下,Feign通过application/json方式传输数据,导致表单提交时参数丢失。通过创建FeignConfig配置类并定义feignFormEncoder编码器,结合SpringFormEncoder实现表单支持。在接口中使用@ModelAttribute注解且不添加@RequestBody,并设置consumes=MediaType.APPLICATION_FORM_URLENCODED_VALUE属性,即可实现表单提交功能。

风流倜傥唐伯虎的博客 3154

开发技术-SpringBoot FeignClient 调用application/x-www-form-urlencoded 格式的 POST 请求

【代码】开发技术-SpringBoot FeignClient 调用application/x-www-form-urlencoded 格式的 POST 请求。

JustDI0209的博客 3066

open feign支持调用form-data的接口

【代码】open feign支持调用form-data的接口。

好好学电脑 949

四步解决Feign调用form-data的接口

Feign默认不支持form-data的接口,如果需要支持其实也不难。只要四步。 第一步:创建一个转换器。 @Configuration public class FeignMultipartSupportConfig { @Bean @Primary @Scope("prototype") public Encoder multipartFormEncoder() { return new SpringFormEncoder(); } } 第...

翼望空间@CSDN.net 9581

Spring boot + Spring Cloud框架下通过Feign进行跨服务传递MultipartFile文件

在我的写的方法中.涉及到了跨服务上传文件的方法,但是这个时候经过尝试跨服务无法传递文件本来我是已经放弃了,改成了数组传递.不过后面又找到了可以支持传递文件的方式方法如下:最开始的时候Spring cloud中,Feign本身是不支持上传文件的能力的,估约1年前.要想实现这个方法,需要自己去编写Encoder的,然而这种情况编写起来比较复杂麻烦.我也在网上看过编写Encoder的方式,情况比较麻烦....

Dragon_MD的博客 1万+

feign 客户端 同时支持文件上传和实体类作为参数接收

想让feign 客户端,这样既支持文件上传也支持实体类作为参数接收, 并放一个实体RequestBody 中. 一直报错: 2020-03-25 19:02:02.388 ERROR 3217 --- [ZXr4fAmWFX][io-22223-exec-1] core.ErrorHandler : system occur error: feign.codec.Enco...

AlphaF 4826

服务间通过Feign相互调用报错,参数是MultiparFile、参数是POJO报错

服务间通过Feign调用上传文件不支持(FileUploadException: the request was rejected because no multipart boundary),并且参数是实体也不支持(feign.codec.EncodeException: class XxxxVo is not a type supported by this encoder.)

Benjamin 2172

Spring Cloud Feign 上传头像文件 MultipartFile

(img-oC2ZBAT3-1713392198969)]也可以不引入maven。

2301_76278775的博客 1017

Springcloud feign文件上传报错FileUploadException: the request was rejected because no multipart boundary

在选择springcloud作为微服务框架时,我们使用远程RPC调用接口,此时我们选择Feign声明式服务调用,简单易用, 可以直接继承接口,注入使用,那此时在文件上传时,变出现了问题,FeignClient调用接口时不支持上传文件;于是找到了githup找到一个开源的工具包, 引入在pom文件中加入 <!--解决不能传递表单参数的问题--> ...

xinlus的博客 4680

Feign实现文件上传下载

Feign框架对于文件上传消息体格式并没有做原生支持,需要集成模块feign-form来实现。 独立使用Feign 添加模块依赖: <!-- Feign框架核心 --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-core</artifactId> <version>11.1</versi..

HongYu012的博客 3580

Feign对接Post请求,application/x-www-form-urlencoded类型请求接口

我们最常用的接口请求方式,一个是Get一个是Post请求类型是json的,这两个是最常规的,只要按照官方配置Get对应@GetMapping,参数使用@RequestParam标识,Post+json的接口使用@PostMapping然后接口使用@RequestBody标识一般也不会有什么问题。 但是,如果需要对接的接口是多个参数,Conten-Type还是application/x-www-form-urlencoded类型,同时要求多个参数都要放到requestBody里面的情况就需要稍微注意了,要知

weixin_39386430的博客 8156

OpenFeign支持application/x-www-form-urlencoded类型数据

OpenFeign支持application/x-www-form-urlencoded类型数据(Feign自动添加的内容引起的血案)

u011641070的博客 1647

openfeign使用表单形式提交

【代码】openfeign使用表单形式提交

liuqiang211的专栏 951

feign技巧 - form方式传值(上传文件)

feign技巧 - form方式传值

夫礼者的专栏 3289

Feign使用Form表单提交请求不成功的问题

最近使用feign调用一个邮件服务接口,接口请求时Content-Type必须为 application/x-www-form-urlencoded;charset=utf-8 但是使用 consumes = "application/x-www-form-urlencoded;charset=UTF-8",虽然Content-Type修改为application/x-www-form-urle...

dyhdsg123456的博客 1032

Spring Cloud Feign Post 表单请求

问题 在使用SpringCloud时,发现Feign不能发送POST表单请求 注意:在SpringBoot/Cloud环境中,使用的FeignClient,在不做额外设置的情况下,只能使用MVC的注解,也就是@RequestMapping和@RequestBody或者@RequestParam。 FeignClient 的 接口尝试以 POST表单发送请求的写法: @RequestMapping(value="/someThing/someMethod", method=RequestMethod

weixin_42073629的博客 2924

feign 修改为表单提交 识别失败

feign 修改form 表单提交 失败

yjq907的博客 384

柱状图_精美学习课件ppt

柱状图_精美学习课件ppt

上一篇: feign入门教程
下一篇: java8 lambda表达式原理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值