spring cloud feign client中 GET方法传输复杂对象的解决方案
在spring cloud feign client中,一般复杂对象只能用Post方法加上@RequestBody来传送。但如果只是读取操作,理应用Get方法。Spring的RestController是支持get方法传递复杂对象的,但用了feign client就发现,如果GET参数为复杂对象,它会自动转成post,这很不友好,也不规范。网上方法比较多,但要么是转成了POST,要么代码侵入性很强,需要修改feign的代码。于是我自己研究了一下spring-cloud-feign,终于找到了优雅的方法,让spring-cloud-feign支持GET方法传递复杂对象。
示例要传输的复杂对象如下:
public class TestGroup {
private String name;
private int ver = 1;
private List<TestItem> items;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<TestItem> getItems() {
return items;
}
public void setItems(List<TestItem> items) {
this.items = items;
}
private int getVer() {//这个申明为私有,故意让它不应该传递的
return ver;
}
public void setVer(int ver) {
this.ver = ver;
}
}
public class TestItem {
private String itemCode;
public String getItemCode() {
return itemCode;
}
public void setItemCode(String itemCode) {
this.itemCode = itemCode;
}
}
接口申明:
@GetMapping(value = "/test/testgroups/sample")
public List<TestGroup> searchBySample(TestGroup group);
为了要让spring-cloud-openfeign能够支持这个API,下面是支持类。
1. 建立一个Annotation
这个Annotation用于标识这个对象是要在Get请求时,转换成Spring RestController能接收的参数。
@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GetParam {
Class<?> value() default Object.class;
}
然后接口申明改变为:
@GetMapping(value = "/test/testgroups/sample")
public List<TestGroup> searchBySample(@GetParam TestGroup group);
2. 实现这个Annotation的处理类
本类处理@GetParam。
/**
* 用于处理{@link GetParam}
*/
public class GetParamParameterProcessor implements AnnotatedParameterProcessor {
private static final Class<GetParam> ANNOTATION = GetParam.class;
@Override
public Class<? extends Annotation> getAnnotationType() {
return ANNOTATION;
}
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
int parameterIndex = context.getParameterIndex();
// Class<?> parameterType = method.getParameterTypes()[parameterIndex];
MethodMetadata data = context.getMethodMetadata();
String paramName = "s";// 用一个假参数骗过feign把本参数当个RequestParameter处理
context.setParameterName(paramName);
Map<Integer, Expander> indexToExpander = data.indexToExpander();
indexToExpander.put(parameterIndex, new GetParameterExpander());
data.indexToEncoded().put(parameterIndex, Boolean.FALSE);
RequestTemplate template = data.template();
Collection<String> query = context.setTemplateParameter(paramName, template.queries().get(paramName));
template.query(paramName, query);
return true;
}
}
下面这个类是让feign处理@GetParam时,把复杂对象转成请求串。
import com.ibm.iisp.common.util.UriUtils;
import feign.Param.Expander;
public class GetParameterExpander implements Expander {
@Override
public String expand(Object value) {
return UriUtils.toQueryString(value, "");
}
}
假如输入的对象为:
TestGroup group = new TestGroup();
group.setName("Test group1.");
ArrayList<TestItem> items = new ArrayList<>();
group.setItems(items);
TestItem item = new TestItem();
item.setItemCode("Item1");
items.add(item);
item = new TestItem();
item.setItemCode("Item2");
items.add(item);
转换结果如:items[0].itemCode=Item1&items[1].itemCode=Item2&name=Testgroup1
3. 改变Spring-boot的配置,加载处理类
我们在Spring-boot配置类中,改写ParameterProcessor的配置,增加我们的GetParamParameterProcessor:
@Configuration
public class FegnConsumerAppConfig {
Logger log = LoggerFactory.getLogger(getClass());
@Bean
public PathVariableParameterProcessor getPathVariableParameterProcessor() {
return new PathVariableParameterProcessor();
}
@Bean
public RequestParamParameterProcessor getRequestParamParameterProcessor() {
return new RequestParamParameterProcessor();
}
@Bean
public RequestHeaderParameterProcessor getRequestHeaderParameterProcessor() {
return new RequestHeaderParameterProcessor();
}
@Bean
public QueryMapParameterProcessor getQueryMapParameterProcessor() {
return new QueryMapParameterProcessor();
}
@Bean
public GetParamParameterProcessor getGetParamParameterProcessor() {
return new GetParamParameterProcessor();
}
}
最后启动你的Spring-boot用feign client传入一个TestGroup即可。