一、JsonResult 在 OpenFeign 微服务调用的问题
我们在使用 Spring Cloud 微服务的时候,通常将返回结果使用一个JsonResult 类进行封装,例如如下的格式:
public class JsonResult {
/* 响应码,200为成功 */
private Integer code;
/* 失败时的具体失败信息 */
private String message;
/* 成功时的数据对象 */
private T data;
}
而调用方在使用Spring Cloud OpenFeign定义的客户端调用远程服务时,由于远程微服务接口的返回值也是 JsonResult 对象,这样本地的接口也需要使用 JsonResult 进行接收,这增加了额外的Result类重新拆开等工作。
有没有办法实现一些自定义的逻辑,比如将统一返回的Result类重新拆开仅返回对应的业务对象,或者对特定的响应码进行处理等等?
二、自定义 OpenFeign 响应解码器
为了实现上述功能,我们就需要改造默认的Decoder。Spring Cloud OpenFeign允许我们在定义一个FeignClient 的时候,指定一个额外的配置类,比如:
@FeignClient(
name = "xxx-base",
path = "/api/base",
configuration = CustomizedConfiguration.class /* 自定义配置类 */
)
public interface RemoteUserService {
//..
}
我们可以在 CustomizedConfiguration 中定义一个自己的 Decoder 来覆盖默认的配置。
Spring Cloud 对 Feign的封装和默认配置可以查看官方文档。
自定义的 Decoder 需要实现feign.codec.Decoder接口,也可以参考默认的Decoder的实现逻辑(org.springframework.cloud.openfeign.support.ResponseEntityDecoder),
下面的实现可以对统一返回值Result类的解包,并对异常返回进行处理:
public class CustomizedConfiguration{
@Bean
public Decoder feignDecoder() {
return new OpenFeignResultDecoder();
}
}
public class OpenFeignResultDecoder implements Decoder {
@Resource
ObjectMapper objectMapper;
@Override
public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
String resultJson = this.getResponseBody(response);
try {
JavaType rawType = objectMapper.getTypeFactory().constructType(type);
JavaType resultType = objectMapper.getTypeFactory().constructParametricType(JsonResult.class, rawType.getRawClass());
JsonResult> jsonResult = objectMapper.readValue(resultJson, resultType );
if (jsonResult.getCode() != HttpStatus.OK.value()){
throw new DecodeException(
response.status(),
jsonResult.getMessage(),
response.request());
}
return jsonResult.getData();
} catch (Exception ex){
throw new IllegalArgumentException("对象转换失败: " + ex.getMessage());
}
}
/*
* 将 response body 解析为 string
*/
private static String getResponseBody(Response response) throws IOException {
Response.Body resBody = response.body();
if (Objects.isNull(resBody)){
throw new DecodeException(
response.status(),
"返回体为空",
response.request());
}
String jsonStr;
char[] buffer = new char[1024*4];
int len;
try (
Reader reader = resBody.asReader(StandardCharsets.UTF_8);
StringWriter strWriter = new StringWriter()
){
while ((len = reader.read(buffer)) != -1){
strWriter.write(buffer, 0, len);
}
jsonStr= strWriter.toString();
}
return jsonStr;
}
}
实现了Decoder之后,只需要将其配置到CustomizedConfiguration中即可。
三、为 FeignClient 注册全局配置
注意如果CustomizedConfiguration添加了@Configuration的注解,则会成为Feign Client构建的默认配置,这样就不需要在每个@FeignClient注解中都去指定配置类了:
@Configuration
public class OpenFeignConfig {
@Bean
public Decoder feignDecoder() {
return new OpenFeignResultDecoder();
}
}
@FeignClient(
name = "xxx-base",
path = "/api/base"
)
public interface RemoteUserService {
//..
}
四、使用 OpenFeign 远程服务示例
添加了自定义的Decoder之后,如果一个远程接口的定义是这样的:
@FeignClient(
name = "xxx-base",
path = "/api/base"
)
public interface RemoteUserService {
@GetMapping(value = "/user/detail/{userId}")
public User getUserDetailById(@PathVariable Integer userId)
}
// ...
@Resource
RemouteUserService userService
public void demoUserService(int userId){
User user = userService.getUserDetailById(userId);
// ....
}
到此这篇关于SpringCloud OpenFeign 自定义响应解码器的文章就介绍到这了,更多相关SpringCloud OpenFeign解码器内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!
