当 Netflix Feign 发送请求和接收响应时,需要对请求和响应的数据进行编码和解码。默认情况下,Netflix Feign 使用一些内置的编码器和解码器,如 JacksonEncoder 和 JacksonDecoder,但在某些场景下,我们可能需要自定义编码和解码的逻辑。下面将介绍如何自定义编码器和解码器:
要自定义编码器,需要实现 feign.codec.Encoder 接口。这个接口有一个 encode 方法,它接受三个参数:
void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
其中:
object 要编码的对象Object。
template 请求模板 RequestTemplate。
bodyType 请求的类型 Type。
例如,假设我们要自定义一个编码器,将一个自定义的 User 对象编码为 JSON 格式并发送到服务端。首先,我们需要定义 User 类:
Feign.builder() .encoder(new Encoder() { @Override public void encode(Object o, Type type, RequestTemplate requestTemplate) throws EncodeException { if(Objects.nonNull(o)) { requestTemplate.body(JSONObject.toJSONString(o)); } else { requestTemplate.body(""); } } }) .contract(new Contract.Default()) .target(EncodeDecodeFeign.class, "http://localhost:8090");
自定义解码器需要实现 feign.codec.Decoder 接口。这个接口有一个 decode 方法,它接受三个参数:
Object decode(Response response, Type type) throws IOException, DecodeException, FeignException;
其中:
response 响应体Response。
type 类型Type。
例如,假设服务端返回的 JSON 数据中包含一些额外的字段或者格式与默认的不同,我们需要自定义解码器来正确解析数据。以下是一个简单的自定义解码器示例,用于将 JSON 数据解析为 User 对象:
Feign.builder() .decoder(new Decoder(){ @Override public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); outputStream.write(response.body().asInputStream().readAllBytes()); String body = outputStream.toString(StandardCharsets.UTF_8); if (StringUtils.hasText(body)) { return JSONObject.parseObject(body, Class.forName(type.getTypeName())); } else { return null; } } catch (Exception e) { throw new DecodeException(response.status(), "Failed to decode response body: " + e.getMessage(), response.request(), e); } } }) .contract(new Contract.Default()) .target(EncodeDecodeFeign.class, "http://localhost:8090");
下面是自定义编码和解码器示例的完整代码:
(1)服务接口代码:
@PostMapping("/encode") public User encode(@RequestBody User user) { System.out.println(user); return user; }
(2)定义 Feign 客户端:
package com.hxstrive.demo_netflix_feign.feign; import com.alibaba.fastjson.JSONObject; import com.hxstrive.demo_netflix_feign.entity.User; import feign.*; import feign.codec.DecodeException; import feign.codec.Decoder; import feign.codec.EncodeException; import feign.codec.Encoder; import org.springframework.util.StringUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.Objects; /** * 请求编码/解码器 * @author HuangXin * @since 1.0.0 2024/11/1 16:34 */ public interface EncodeDecodeFeign { @RequestLine("POST /simple/encode") @Headers("Content-Type: application/json") User encode(User user); // 自定义编码/解码器 static EncodeDecodeFeign create() { return Feign.builder() .encoder(new Encoder() { @Override public void encode(Object o, Type type, RequestTemplate requestTemplate) throws EncodeException { if(Objects.nonNull(o)) { requestTemplate.body(JSONObject.toJSONString(o)); } else { requestTemplate.body(""); } } }) .decoder(new Decoder(){ @Override public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); outputStream.write(response.body().asInputStream().readAllBytes()); String body = outputStream.toString(StandardCharsets.UTF_8); if (StringUtils.hasText(body)) { return JSONObject.parseObject(body, Class.forName(type.getTypeName())); } else { return null; } } catch (Exception e) { throw new DecodeException(response.status(), "Failed to decode response body: " + e.getMessage(), response.request(), e); } } }) .contract(new Contract.Default()) .target(EncodeDecodeFeign.class, "http://localhost:8090"); } }
(3)调用 Feign 客户端:
@GetMapping("/") public String index() { return EncodeDecodeFeign.create().encode(User.builder().id(100L).name("Tom").age(20).build()).toString(); }
输出结果如下图: