feign form支持
来源:互联网 发布:淘宝静物拍摄相机 编辑:程序博客网 时间:2024/06/01 21:57
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代码 FormContract和FormEncoder
生成feign客户端的方式请见FeinFactory
调用代码变为
@Headers("Content-Type: application/x-www-form-urlencoded") @RequestLine("POST /test2") public BaseResponse test4(@FormBean Demo demo);
第一次扩展没中文文档的开源框架,英语不好,水平有限,理解有问题的地方请不吝指出。
- feign form支持
- Feign Hystrix 支持
- Feign对Hystrix的支持
- spring cloud-Feign的Hystrix支持
- 十四、断路器-Hystrix 对 Feign 的支持
- Feign
- Feign
- 【SpringCloud】(十四):Feign对Hystrix的支持 fallbackFactory
- 支持列表的Form属性
- HTML 5 form属性支持
- springCloud学习02之断路器Hystrix-turbine监控-ribbo/feign对Hystrix的支持
- 【SpringCloud】(十三):全局和单个禁用Feign Client对Hystrix的支持
- 使用feign作为客户端来消费服务,提供负载均衡和断路器支持
- Feign介绍
- Feign logging
- Feign介绍
- Feign基础教程
- feign入门教程
- 搜狗面试题-武汉-2017
- activemq安全设置
- 【从AlphaGo的恐怖进化谈起】附机器学习入门教程
- 对输入的字符串按字典顺序输出所有的全排列,字符串可以由重复字符
- EXCEL-VBA:引用SHEET中的TextBox1(文本框)的值
- feign form支持
- 决策树——中文版
- ubuntu系统安装好后,输入法设置失败(更新-药到病除)
- 去除Xcode签名 + Alcatraz 插件安装?
- MySQL左连接、右连接、等值连接
- 前端JS跨域请求
- Android 丰富的程序员在开发一个应用时不会犯的错误
- OpenStack社区组件-网络和内容发布
- Win10输入法不能用切换不了