这些问题只是本人觉得奇葩,也许不是FeignClient的问题,whatever,只是做个记录。
1、一言不合就POST
/**
* 查询单个档案(根据档案类型名称)
* 用法:GET http://<host>:<port>/v1.0/devices/archives2/{archiveName}/{deviceId}
* @param deviceId 设备id
* @param archiveName 档案类型名称
* @param appToken 应用token
* @return 返回查询结果
*/
@Headers("app-token:{appToken}")
@RequestLine("GET /devices/archives2/{archiveName}/{deviceId}")
DeviceArchive findSingleArchiveByDeviceId2(@Param("appToken")String appToken,
@Param("archiveName")String archiveName,
@Param("deviceId")String deviceId);
对于上面这个接口,能看出哪里有错吗?服务端返回的报错:
Exception in thread "main" feign.FeignException: status 405 reading Mongo#findSingleArchiveByDeviceId2(String,String,String); content:
{"timestamp":1488507926013,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/devices/archives2/air_container/4661125"}
at feign.FeignException.errorStatus(FeignException.java:62)
at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:91)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:134)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76)
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:103)
at com.sun.proxy.$Proxy4.findSingleArchiveByDeviceId2(Unknown Source)
at com.chinamobile.iot.App.main(App.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
第一反应是见了鬼了,明明是GET,怎么会提示”Request method ‘POST’ not supported”。搞了半天才发现是@Herders里边的键值对冒号后面必须有个空格!正确的写法:
@Headers("app-token: {appToken}")
另外,如果GET方法有一堆参数:
/**
* 查询单个设备数据
* 用法:GET http://<host>:<port>/v1.0/devices/datas/{deviceId}
* @param deviceId 设备ID
* @param appToken 应用Token
* @param deviceDataId 数据集合的UUID
* @param deviceDataName 数据名
* @param filters 过滤条件
* @param limit 最大查询数据量
* @param startTime 开始时间
* @param endTime 结束时间
* @return 返回查询结果
*/
想要设置一个request实体类,把所有参数都包进去,放到HTTP 的body来上传,也会被当成POST。一句话:GET参数不支持复杂对象(只支持String、Integer这些简单对象)
2、传的是NULL,收到的是参数名
例子如下:
@RequestLine("POST /mongodb/datas")
boolean addBatchDatas(@Param("app-token") String appToken, AddBatchDataRequest addBatchDataRequest);
如果传参的时候,因为某些原因给appToken传的是null(并非有意要传null,有时是程序出现了bug)
mongo.addBatchDatas(null, addBatchDataRequest); //mongo是已初始化的Feign对象
服务端报错,输出的调试信息如下:
appToken={app-token}
当第一次遇到这个问题的时候,百思不得其解为何appToken会被设为“{app-token}”,调试了半天才发现是设值null时Feign就会传这个,私以为Feign更合理的做法是提示某某参数为null。
3、服务是POST,Client错写为GET
服务端是POST:
@RequestMapping(value = "{productId}", method = RequestMethod.GET)
ProductInfo getProduct(@PathVariable("productId") Integer productId);
@RequestMapping(value = "getByProductKey", method = RequestMethod.POST)
public ProductInfo getByProductKey(@RequestParam("productKey") String productKey);
Client错把getByProductKey写为GET
@RequestMapping(value = "{productId}", method = RequestMethod.GET)
ProductInfo getProduct(@PathVariable("productId") Integer productId);
@RequestMapping(value = "getByProductKey", method = RequestMethod.GET)
ProductInfo getByProductKey(@RequestParam("productKey") String productKey);
客户端调用时报错:
"message":"Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: \"getByProductKey\""
服务端打印的调试信息也很奇怪,明明调的是getByProductKey,怎么变成了getProduct:
GET 192.168.1.115:9000/products/getByProductKey?productKey=ukyUPF7S started, method=getProduct
4、直接返回NULL
如果忘了加@EnableFeignClients,则会直接调callback,导致返回null。