http是第三方接口调用的常用工具,该组件通过对常见的http客户端工具进行统一的整合,参考了feign的上下文和springmvc的拦截器等相关设计思路,设计了相应的Context用于对请求上下文的相关配置进行统一的配置,避免客户端配置混乱配置,保证了项目的统一配置,并内置了相应拦截器,用于客户端自定义相关的业务逻辑,比如:通过模仿feign的拦截器,解决了feig三方接口调用时无token的尴尬.
一 基本原理
- 客户端发起http调用时,通过请求执行器,执行底层请求逻辑
- 从请求的上下文中获得相应编码器,对body进行统一的序列化处理
- 获得相应的前置处理器,执行自定义的业务前置处理(如token等公用参数的绑定)
- 调用httprequestclient执行http请求(更具项目的自身需求,配置相应的客户端,默认为:JDK HttpURLConnection)
- 请求发生异常,执行requestExceptionHandler中handler逻辑
- 解析当前响应状态码,状态码不为200执行responseErrorHandler逻辑
- 根据当前请求是否配置相应的响应前置处理器,做响应前置处理,没有直接通过解码器反序列化后相应当前请求的数据
二 客户端调用
2.1 根据项目的实际需求配置核心参数
详见源码具体配置
2.2 根据项目的实际需求选择相应的http客户端
默认http客户端
2.3 引入相应的组件
<dependency>
<groupId>com.cncloud</groupId>
<artifactId>shell-platform-common-http-client</artifactId>
</dependency>
2.4 bean方式调用
@RestController
@RequestMapping("/client")
@Slf4j
public class HttpClientController {
@Autowired
private HttpRequestExecutor httpRequestExecutor;
public static final String LOGIN_URL = "https://develop.shequtalk.com/auth/oauth/token";
public static final String GET_URL = "https://develop.shequtalk.com/admin/user/getUserListByDept";
@PostMapping("/testPost")
@Inner(value = false)
public void testPost(){
HttpRequest request = getLoginRequest();
final R result = httpRequestExecutor.execute(request);
log.info("login result : {}",result);
}
@GetMapping("/testGet")
@Inner(value = false)
public void testGet(){
HttpRequest request = new BasicRequestBuilder(GET_URL)
.header("tenant-id", "6054656364413952")
.header("authorization", "Bearer 8350ebce-1f98-41e3-a30f-69f4f6702119")
.method(RequestMethod.GET)
.query("sysDeptId", "6054656376194048")
.query("current", 1)
.query("size", 10)
.resultType(R.class)
.build();
R result = httpRequestExecutor.execute(request);
log.info("query result: {}",result);
}
private HttpRequest getLoginRequest() {
HashMap<String, Object> bodyMap = new HashMap<>();
bodyMap.put("username","szs");
bodyMap.put("password","rKu1/348LvKp0rsVC06eCA==");
bodyMap.put("grant_type","password");
HttpRequest request = new BodyRequestBuilder<>(LOGIN_URL)
.header("tenant-id", "6054656364413952")
.header("authorization","Basic c2hlbGxwYzpzaGVsbHBj")
.method(RequestMethod.POST)
.form(bodyMap)
.resultType(R.class)
.build();
return request;
}
}
采用相应的构造者构建相应的请求
2.5 非bean方式调用
@RestController
@RequestMapping("/client")
@Slf4j
public class HttpClientController {
@Autowired
private HttpRequestExecutor httpRequestExecutor;
public static final String LOGIN_URL = "https://develop.shequtalk.com/auth/oauth/token";
public static final String GET_URL = "https://develop.shequtalk.com/admin/user/getUserListByDept";
@PostMapping("/testNoBeanWithHutool")
@Inner(value = false)
public void testNoBeanWithHutool(){
HttpRequestClient httpRequestClient = new HutoolRequestClient();
final HttpRequestExecutor httpRequestExecutor = initHttpRequestExecutor(httpRequestClient);
HttpRequest request = getLoginRequest();
final R result = httpRequestExecutor.execute(request);
log.info("login result : {}",result);
}
private HttpRequestExecutor initHttpRequestExecutor(HttpRequestClient httpRequestClient) {
return new DefaultHttpRequestExecutor(getContext(), httpRequestClient);
}
private Context getContext() {
TimeoutSetting timeoutSetting = new TimeoutSetting();
Encoder encoder = new DefaultEncoder();
Decoder decoder = new DefaultDecoder();
return new DefaultContext(timeoutSetting, decoder, encoder);
}
private HttpRequest getLoginRequest() {
HashMap<String, Object> bodyMap = new HashMap<>();
bodyMap.put("username","szs");
bodyMap.put("password","rKu1/348LvKp0rsVC06eCA==");
bodyMap.put("grant_type","password");
HttpRequest request = new BodyRequestBuilder<>(LOGIN_URL)
.header("tenant-id", "6054656364413952")
.header("authorization","Basic c2hlbGxwYzpzaGVsbHBj")
.method(RequestMethod.POST)
.form(bodyMap)
.resultType(R.class)
.build();
return request;
}
}
三 核心API
/**
* http请求执行器
* @author likun
* @date 2022/7/25 10:14
*/
public interface HttpRequestExecutor {
/**
* 执行http请求(异常处理器及前置拦截器均可传入)
* @param request 核心请求
* @param requestExceptionHandler 请求发送异常处理器
* @param responseErrorHandler 相应异常处理器
* @param requestInterceptor 请求拦截器
* @param responseInterceptor 相应拦截器
* @param <T>
* @return
*/
<T> T execute(HttpRequest request, RequestExceptionHandler requestExceptionHandler, ResponseErrorHandler responseErrorHandler,
RequestInterceptor requestInterceptor, ResponseInterceptor responseInterceptor);
/**
* 执行http请求(使用默认的拦截器和异常处理器)
* @param request
* @param <T>
* @return
*/
default <T> T execute(HttpRequest request){
return execute(request,null,null,null,null);
}
/**
* 执行http请求 (可以传入请求异常及响应状态码非200的请求处理器)
* @param request
* @param requestExceptionHandler
* @param responseErrorHandler
* @param <T>
* @return
*/
default <T> T execute(HttpRequest request, RequestExceptionHandler requestExceptionHandler, ResponseErrorHandler responseErrorHandler){
return execute(request, requestExceptionHandler, responseErrorHandler, null, null);
}
/**
* 执行http请求(可以传入请求前置拦截器及响应解码的前置拦截器)
* @param request
* @param requestInterceptor
* @param responseInterceptor
* @param <T>
* @return
*/
default <T> T execute(HttpRequest request, RequestInterceptor requestInterceptor, ResponseInterceptor responseInterceptor) {
return execute(request, null, null, requestInterceptor, responseInterceptor);
}
}
四 项目源码
由于改组件的源码较长,这里不在文章中展示,需要的可以私信笔者.