1. 问题描述
问题就是通过FeignClient调用某个接口的时候,请求参数明明有值,服务端拿到的请求对象却为null。
2. 问题分析
FeignClient接收不到参数可能会存在以下原因, 1.客户端没有传值。 2.客户端与服务端参数类型不一致导致服务端接收不到。 3.请求对象中含有以is开头的参数,FeignClient框架序列化会过滤掉。 4.FeignClient框架问题,考虑其他框架如httpclient,hutool的httpUtil等。
3. 测试验证
3.1 环境依赖
spring-boot是2.3.2.RELEASE版本,openfeign是2.2.5.RELEASE。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/>
</parent>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
3.2 代码实现
FeignClientReqDTO
package com.jerry.market.entity;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
@Data
public class FeignClientReqDTO implements Serializable {
private static final long serialVersionUID = 4804236268827809329L;
private String tenantId;
private String bizId;
private boolean mark;
private Boolean isFilterMark;
@JsonProperty("isFilterMarkJson")
private boolean isFilterMarkJson;
private boolean isFilterMarkSimple;
private String start;
private String isDeleted;
@JsonProperty("isDeletedJson")
private String isDeletedJson;
private Integer tools;
private int toolCount;
private int isToolNumber;
@JsonProperty("isToolNumberJson")
private int isToolNumberJson;
}
FeignClientServerDTO
package com.jerry.market.entity;
import lombok.Data;
import java.io.Serializable;
@Data
public class FeignClientServerDTO implements Serializable {
private static final long serialVersionUID = 4804236268827809329L;
private String tenantId;
private String bizId;
private Boolean mark;
private Boolean isFilterMark;
private Boolean isFilterMarkJson;
private Boolean isFilterMarkSimple;
private String start;
private String isDeleted;
private String isDeletedJson;
private Integer tools;
private Integer toolCount;
private Integer isToolNumber;
private Integer isToolNumberJson;
}
FeignController
package com.jerry.market.controller;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jerry.market.entity.*;
import com.jerry.market.integration.FeignClientService;
import com.jerry.market.integration.TemplateClientService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/feign")
@Api(tags = "FeignController", description = "Feign控制器")
public class FeignController {
@Resource
private FeignClientService feignClientService;
@PostMapping("/getFeignServer")
@ApiOperation(value = "FeignClient服务端")
public Response getFeignServer(@RequestBody FeignClientServerDTO feignClientServerDTO) {
System.out.println("【FeignClient服务端】请求参数:" + feignClientServerDTO);
return Response.success(feignClientServerDTO);
}
@GetMapping("/getFeignClientByObject")
@ApiOperation(value = "FeignClient根据请求对象查询客户端")
public Response getFeignClientByObject() {
FeignClientReqDTO dto = buildFeignClientReqDTO();
System.out.println("【FeignClientObject客户端】请求参数:" + dto);
JSONObject ret = feignClientService.getFeignClientByObject(dto);
System.out.println("【FeignClientObject客户端】响应参数:" + ret);
return Response.success(ret);
}
@GetMapping("/getFeignClientByMap")
@ApiOperation(value = "FeignClient根据请求集合查询客户端")
public Response getFeignClientByMap() {
Map<String, Object> map = buildFeignClientReqMap();
System.out.println("【FeignClientMap客户端】请求参数:" + map);
JSONObject ret = feignClientService.getFeignClientByMap(map);
System.out.println("【FeignClientMap客户端】响应参数:" + ret);
return Response.success(ret);
}
@GetMapping("/getHttpClientByObject")
@ApiOperation("查询模板通过HttpClient")
public Response<JSONObject> getHttpClientByObject() {
HttpRequest post = HttpUtil.createPost("http://localhost:8090/feign/getFeignServer");
FeignClientReqDTO dto = buildFeignClientReqDTO();
post.body(JSON.toJSONString(dto));
System.out.println("【查询模板通过HttpClient】请求参数:" + dto);
HttpResponse response = post.execute();
System.out.println("【查询模板通过HttpClient】响应参数:" + response.body());
return Response.success(JSON.parseObject(response.body()));
}
private FeignClientReqDTO buildFeignClientReqDTO() {
FeignClientReqDTO dto = new FeignClientReqDTO();
dto.setTenantId("123456789");
dto.setBizId("helloWorld");
dto.setIsFilterMark(false);
dto.setFilterMarkJson(false);
dto.setFilterMarkSimple(false);
dto.setMark(false);
dto.setStart("1");
dto.setIsDeleted("2");
dto.setIsDeletedJson("3");
dto.setTools(4);
dto.setToolCount(5);
dto.setIsToolNumber(6);
dto.setIsToolNumberJson(7);
return dto;
}
private Map<String, Object> buildFeignClientReqMap() {
Map<String, Object> map = new HashMap<>(9);
map.put("tenantId", "123456789");
map.put("bizId", "helloWorld");
map.put("isFilterMark", false);
map.put("isFilterMarkJson", false);
map.put("isFilterMarkSimple", false);
map.put("mark", false);
map.put("start", "1");
map.put("isDeleted", "2");
map.put("isDeletedJson", "3");
map.put("tools", "4");
map.put("toolCount", "5");
map.put("isToolNumber", "6");
map.put("isToolNumberJson", "7");
return map;
}
}
FeignClientService
package com.jerry.market.integration;
import com.alibaba.fastjson.JSONObject;
import com.jerry.market.entity.FeignClientReqDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Map;
@Component
@FeignClient(url = "http://localhost:8090/feign", name = "FeignClientService")
public interface FeignClientService {
@PostMapping(value = "/getFeignServer")
JSONObject getFeignClientByObject(@RequestBody FeignClientReqDTO dto);
@PostMapping(value = "/getFeignServer")
JSONObject getFeignClientByMap(@RequestBody Map<String, Object> map);
}
4. 结论总结
通过Feign调用接口,请求参数接收不到值问题 1.使用Feign与hutool的httpUtil调用,且请求对象含有以is开头的命名的布尔类型参数,且请求基本类型,服务端包装类型,接收不到值。 2.客户端通过集合作为请求对象不存在这种问题,即使请求参数中含有以is开头的命名的参数。 3.客户端通过给以is开头的参数添加序列化注解@JsonProperty(“isDeletedJson”) 4.客户端对于除了布尔类型以外其他类型即使含有以is开头的参数也不会受影响
5. 阿里开发手册
阿里开发手册-泰山版,命名规范第8条。
8.【强制】POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。 说明:在本文 MySQL 规约中的建表约定第一条,表达是与否的值采用 is_xxx 的命名方式,所以,需要在设置从 is_xxx 到 xxx 的映射关系。
反例:定义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),框架在反向解析的时候,“误以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。
|