基于SB/SC做微服务化改造,服务之间的HTTP请求大多是基于FeignClient来完成。本文对个人工作中遇到的使用方式做一个小结。
相关maven配置
io.github.openfeign: feign-core :11.8
io.github.openfeign: feign-form :3.8.0
org.springframework.springcloud:spring-cloud-starter-openfeign:3.1.3
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name= "testSpi")
public interface TestSpi {
@GetMapping(path = "/api/v0/user")
public User getUser(@RequestParam("username") String username,
@RequestParam("md5") String md5);
}
举例:
username=“Good Morning”
md5=“md 5”
实际请求url如下,参数做了URLEncode处理
GET /api/v0/user?username=Good%20Morning&md5=md%205
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name= "testSpi")
public interface TestSpi {
@GetMapping(path = "/api/v0/user")
public User getUser(@RequestParam Map<String,Object> paramMap);
}
// 其中 paramMap的内容
/**
{
"username": "Good Morning",
"md5": "md 5"
}
*/
实际效果
GET /api/v0/user?username=Good%20Morning&md5=md%205
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name= "testSpi")
public interface TestSpi {
@GetMapping(path = "/api/v0/user")
public User getUser(@RequestParam UserVo userVo);
}
/**
其中
userVo = new UserVo("Good Morning", "md 5");
*/
实际效果
GET /api/v0/user?username=Good%20Morning&md5=md%205
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.cloud.openfeign.SpringQueryMap;
@FeignClient(name= "testSpi")
public interface TestSpi {
@GetMapping(path = "/api/v0/user")
public User getUser(@SpringQueryMap(encoded = true) Map<String,Object> paramMap);
}
// 其中 paramMap的内容
/**
{
"username": "Good Morning",
"md5": "md 5"
}
*/
实际效果
GET /api/v0/user?username=Good Morning&md5=md 5
encoded=true,告诉Feign字段值已做urlencode 不需要再encode;
encoded=false, 默认值,告诉Feign字段值未做urlencode, 需要由Feign做urlencode;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name= "testSpi")
public interface TestSpi {
@PostMapping(path = "/api/v0/user")
public User getUser(@RequestBody UserVo userVo);
}
/**
其中
userVo = new UserVo("Good Morning", "md 5");
*/
实际效果
POST /api/v0/userCONTENT-TYPE: application/json
body:
{
“username”: “Good Morning”,
“md5”: “md 5”
}
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name= "testSpi")
public interface TestSpi {
@PostMapping(path = "/api/v0/user", consumer=MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public User getUser(@RequestParam UserVo userVo);
}
/**
其中
userVo = new UserVo("Good Morning", "md 5");
*/
实际效果
POST /api/v0/userCONTENT-TYPE: application/x-www-form-urlencoded
body:
username=Good%20Morning&md5=md%205
有时候服务端提供的接口要求body不做encode,比如server端基于Apache+CGI开发。预期的效果如下
POST /api/v0/user
CONTENT-TYPE: application/x-www-form-urlencoded
body:
username=Good Morning&md5=md 5
我们知道FeignClient是通过Encode来对参数做加工,Encoder内部则是依赖不同的ContentProcessor,对于UrlencodedFormContentProcessor来说默认都是做了encode处理的。因此需要自定义ContentProcessor,进而自定义encoder。
原始的UrlencodedFormContentProcessor
public class UrlencodedFormContentProcessor implements ContentProcessor {
private static final char QUERY_DELIMITER = '&';
private static final char EQUAL_SIGN = '=';
@SneakyThrows
private static String encode (Object string, Charset charset) {
return URLEncoder.encode(string.toString(), charset.name());
}
}
自定义的MyUrlencodedFormContentProcessor
public class MyUrlencodedFormContentProcessor implements ContentProcessor {
private static final char QUERY_DELIMITER = '&';
private static final char EQUAL_SIGN = '=';
@SneakyThrows
private static String encode (Object string, Charset charset) {
return string.toString();
}
}
自定义的MyFormEncoder
public class MyFormEncoder {
public MyFormEncoder(Encoder delegate) {
this.delegate = delegate;
val list = asList(
new MultipartFormContentProcessor(delegate),
// 此处写入自己的Processor
new MyUrlencodedFormContentProcessor()
);
processors = new HashMap<ContentType, ContentProcessor>(list.size(), 1.F);
for (ContentProcessor processor : list) {
processors.put(processor.getSupportedContentType(), processor);
}
}
}
定义configuration
public class TestSpiConfiguration {
@Bean
public Encoder encoder() {
return new MyFormEncoder();
}
}
@FeignClient(name= "testSpi", configuration = TestSpiConfiguration.class)
public interface TestSpi {
@PostMapping(path = "/api/v0/user", consumer=MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public User getUser(@RequestParam UserVo userVo);
}
最终效果达成。
本文对FeignClient在使用过程中遇到几种方式做了小结,期待对你有所帮助。