feign get请求使用对象接收参数报错
我们知道SpringBoot get请求时,参数多的时候我们可以使用请求对象来接收,像下面这样:
@ApiOperation(value = "根据不同参数获取不同表数据", response = PicaResponse.class, notes = "demo")
@GetMapping(value = "/")
public PicaResponse<StudyInfoDto> getMegrezData(MegrezRequest request, HttpServletRequest httpServletRequest) {
return PicaResponse.toResponse(megrezService.get(request));
}
但是feign调用服务的时候是不支持请求对象来接收参数的,当用请求对象接收参数时,调用会报如下错误:
{"timestamp":1482676142940,"status":405,"error":"Method Not Allowed",
"exception":"org.springframework.web.HttpRequestMethodNotSupportedException","messa
ge":"Request method 'POST' not supported","path":"/data"}
调试时发现HttpURLConnection中的一段代码,jdk原生的http请求工具类,Feign默认使用的连接工具实现类
private synchronized OutputStream getOutputStream0() throws IOException {
try {
if(!this.doOutput) {
throw new ProtocolException("cannot write to a URLConnection if doOutput=false - call setDoOutput(true)");
} else {
if(this.method.equals("GET")) {
this.method = "POST";
}
调用时必须要把参数分开来接收,像下面这样:
@FeignClient(value="pica-cloud-demo")
public interface MegrezServiceClient {
@GetMapping(value = "/data")
PicaResponse<StudyInfoDto> getMegrezData(@RequestParam("type") int type,
@RequestParam("doctorId") int doctorId,
@RequestParam("hospitalId") int hospitalId,
@RequestParam("provinceId") long provinceId,
@RequestParam("countryId") int countryId);
}
但是这种方式对于调用方来说显然是不友好的,那我们如何来使用请求对象来接收参数呢,按照下面这样操作即可,将feign默认调用的httpclient替换为apache的httpclient:
1.首先在client服务pom文件添加如下依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>9.3.1</version>
</dependency>
2.在client对应接口接收对象前面加上@RequestBody注解
@GetMapping(value = "/data")
PicaResponse<StudyInfoMapDto> getMegrezData(@RequestBody MegrezListRequest megrezListRequest);
同时在controller相应的接口也要加上@RequestBody注解;
@ApiOperation(value = "根据不同参数获取不同表的排名数据", response = PicaResponse.class, notes = "demo")
@GetMapping(value = "/data")
public PicaResponse<StudyInfoMapDto> getMegrezData(@RequestBody(required = false) MegrezListRequest megrezListRequest, HttpServletRequest request) {
return PicaResponse.toResponse(megrezService.get(megrezListRequest));
}
3.网上有说要在resources目录下面添加application.properties,里面内容是:
feign:
httpclient:
enabled: true
但是我没加依然可以正常调用了。
服务调用服务是没问题了,可是这样有一个问题,我们用postman等类似工具调用的时候是调不通的,因为get请求是不能传请求体参数的,那么怎么样可以传form参数同样可以接收到呢,我加了一步处理,如果是form表单参数的话,将参数封装到请求体参数中,多加的代码如下:
@ApiOperation(value = "根据不同参数获取不同表的排名数据", response = PicaResponse.class, notes = "demo")
@GetMapping(value = "/data")
public PicaResponse<StudyInfoMapDto> getMegrezData(@RequestBody(required = false) MegrezListRequest megrezListRequest, HttpServletRequest request) {
megrezListRequest = (null != megrezListRequest) ? megrezListRequest : MegrezRequestConverter.converterToRequest(request, MegrezListRequest.class);
return PicaResponse.toResponse(megrezService.get(megrezListRequest));
}
转化方法如下:
public static <T> T converterToRequest(HttpServletRequest request, Class<T> clazz) {
Field[] declaredFields = clazz.getDeclaredFields();
T t = null;
try {
t = clazz.newInstance();
for (Field field : declaredFields) {
field.setAccessible(true);
Class<?> type = field.getType();
if (Collection.class.isAssignableFrom(type)) {
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
if (actualTypeArguments.length > 0) {
type = (Class<?>) actualTypeArguments[0];
if (type.equals(Integer.class)) {
List<Integer> list = new ArrayList<>();
String[] s = value.split(",");
for (String v: s) {
list.add(Integer.parseInt(v));
}
field.set(t, list);
}
if (type.equals(Long.class)) {
List<Long> list = new ArrayList<>();
String[] s = value.split(",");
for (String v: s) {
list.add(Long.parseLong(v));
}
field.set(t, list);
}
if (type.equals(String.class)) {
field.set(t, Arrays.asList(value.split(",")));
}
}
}
} else {
if (type.equals(Integer.class)) {
field.set(t, Integer.parseInt(value));
}
if (type.equals(Long.class)) {
field.set(t, Long.parseLong(value));
}
if (type.equals(String.class)) {
field.set(t, value);
}
}
}
} catch (Exception e) {
logger.info("converterToRequest: " + e);
throw new PicaException(PicaResultCode.SYSTEM_INNER_ERROR, e);
}
return t;
}
这样处理完之后,我们既可以把参数分开接收,同时可以使用请求对象接收了。