首先,你的 Result
统一返回类必须是有泛型的如:
@Data @NoArgsConstructor @AllArgsConstructor public class Result{ public static final Integer SUCCESS_CODE = 200; // 访问成功状态码 public static final Integer ERROR_CODE = 500; // 访问失败状态码 private Integer status; // 状态码 private String msg; // 提示消息 private T data = null; public Result(Integer status, String msg) { this.status = status; this.msg = msg; } public static Result ok(Integer status,String msg,Object data){ return new Result(status,msg,data); } public static Result ok(String msg,Object data){ return new Result(SUCCESS_CODE,msg,data); } public static Result ok(T data){ return new Result(SUCCESS_CODE,"操作成功",data); } public static Result ok(){ return new Result(SUCCESS_CODE,"操作成功",null); } public static Result fail(Integer status,String msg){ return new Result(status,msg); } public static Result fail(String msg){ return new Result(ERROR_CODE,msg); } public static Result fail(){ return new Result(ERROR_CODE,"操作失败"); } public static Map ok(Map map){ map.put("status",SUCCESS_CODE); map.put("msg","查询成功"); return map; } public static Map ok(Page pageInfo){ Map map = new HashMap<>(); map.put("status",SUCCESS_CODE); map.put("msg","查询成功"); map.put("count",pageInfo.getTotal()); map.put("data",pageInfo.getRecords()); return map; } }
只有这样,在使用 Openfeign调用远程接口时,返回的 Result
中的 data
数据就不是 Object
类型。
我最开始没有使用泛型,就总是将 Object
类型进行强转,但是一直报错。
但上述条件成立时,你可以设置如下:
@FeignClient(value = "user-service") public interface UserServiceFeign { @GetMapping("/api/users/{username}") ResultgetUserByUsername(@PathVariable("username") String username); }
Feign接口返回的是 Result
这其实挺麻烦的,因为我们其实只需要 User
类,所以我们又要手动的取出 Result
中的 data
数据,每次都这样,就比较麻烦。
这时我们就可以配置 自定义解码器。
@Slf4j @RequiredArgsConstructor public class ResultFeignDecoder implements Decoder { private final ObjectMapper objectMapper; @Override public Object decode(Response response, Type type) throws IOException { log.info("调用自定义Feign解码器"); // 读取响应中数据,将响应体中数据转为字符串 String bodyString = Util.toString(response.body().asReader(Util.UTF_8)); // 解析响应体字符串到 Result 对象 JavaType parameterType = objectMapper.getTypeFactory().constructType(type); JavaType resultType = objectMapper.getTypeFactory().constructParametricType(Result.class, parameterType); Result> result = objectMapper.readValue(bodyString, resultType); // 如果响应状态为失败,抛出业务异常 if (!Result.SUCCESS_CODE.equals(result.getStatus())) { throw new BusinessException(result.getMsg()); } // 从 Result 中提取 data 字段 return result.getData(); } }
@Configuration //只要将其设置为配置类,就全局生效 //否则就必须在 FeignClient 上面加上 //@FeignClient(configuration = FeignConfig.class) public class FeignConfig { @Bean public ResultFeignDecoder resultFeignDecoder(ObjectMapper objectMapper) { return new ResultFeignDecoder(objectMapper); } //创建自定义Feign.Builder,配置decodeVoid(),此配置的含义是当FeignClient接口返回值类型为void时,仍然使用解码器。 @Bean public Feign.Builder decodeVoidFeignBuilder() { return Feign.builder().decodeVoid(); } }
FeignConfig添加 @Configuration
注解,则解码器和Builder会全局生效,也就是对所有的FeignClient生效;如果不添加 @Configuration
注解,则仅对指定的FeignClient生效。
这样配置之后,当调用 Feign 客户端时,如果返回类型为 Result
,则会自动提取 T
类型的数据并返回。
之前为什么我的代码有问题?
我返回的是 Result
没有携带任何的 信息,所以取 data
的时候出问题了,因为Java不知道将 data
转换成什么类型,强行转换也失败。
而现在,我们返回的是 Result
携带了信息 T
,这使得我们Java知道该将 data
转换成什么类型。
最后,我们为了统一的返回处理,避免赘余的取 data
数据的逻辑。创建了一个自定义解码器。直接将 data
中的数据取出来返回。