Preview
Skip to content
CodingDiary
返回

简单 HTTP 工具

编辑页面
导读

JustAuth 强依赖 hutool-http,想换成 OkHttp3 怎么办?simple-http 来解耦!设计思路:定义通用 Http 接口,提供 4 种默认实现(JDK11 HttpClient、OkHttp3、Apache HttpClient、Hutool)。通过 Class.forName() 自动探测依赖,也支持自定义实现。运用了代理模式、委派模式、策略模式。

1. 前言

因为本人是 JustAuth 的主要贡献者之一,JustAuth 里需要和各大平台做 HTTP 交互来换取 Token、用户信息等数据,使用的是 hutool-http来实现 HTTP 请求的发送。前段时间,有朋友提出一个需求:能否使用 OkHttp3 等更优秀的 HTTP 请求工具来替换默认的 hutool-http ?于是写一个 simple-http 的想法就诞生了。

2. 设计思路

2.1. 解耦

simple-http 设计出来就是为了解决 JustAuth 中对 hutool-http 的强耦合,所以需要先找到 JustAuth 中到底使用到了需要哪些 HTTP 请求,找到它们的共性,才能解耦。具体参见下表:

请求类型请求参数类型响应数据类型其他
GETqueryJSON、TEXTHeader、URL Encode
POSTForm、JSONJSON、TEXTHeader、URL Encode

响应的数据类型,有些是格式化的 JSON 数据,有些只是简单的文本信息,但总的来说都是字符串数据,具体的解析,交给使用方去处理。所以,此时通用HTTP的接口就已经可以设计出来了。

/**
 * <p>
 * HTTP 接口
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019/12/24 18:21
 */
public interface Http {
	/**
	 * GET 请求
	 *
	 * @param url URL
	 * @return 结果
	 */
	String get(String url);

	/**
	 * GET 请求
	 *
	 * @param url    URL
	 * @param params 参数
	 * @param encode 是否需要 url encode
	 * @return 结果
	 */
	String get(String url, Map<String, String> params, boolean encode);

	/**
	 * GET 请求
	 *
	 * @param url    URL
	 * @param params 参数
	 * @param header 请求头
	 * @param encode 是否需要 url encode
	 * @return 结果
	 */
	String get(String url, Map<String, String> params, HttpHeader header, boolean encode);

	/**
	 * POST 请求
	 *
	 * @param url URL
	 * @return 结果
	 */
	String post(String url);

	/**
	 * POST 请求
	 *
	 * @param url  URL
	 * @param data JSON 参数
	 * @return 结果
	 */
	String post(String url, String data);

	/**
	 * POST 请求
	 *
	 * @param url    URL
	 * @param data   JSON 参数
	 * @param header 请求头
	 * @return 结果
	 */
	String post(String url, String data, HttpHeader header);

	/**
	 * POST 请求
	 *
	 * @param url    URL
	 * @param params form 参数
	 * @param encode 是否需要 url encode
	 * @return 结果
	 */
	String post(String url, Map<String, String> params, boolean encode);

	/**
	 * POST 请求
	 *
	 * @param url    URL
	 * @param params form 参数
	 * @param header 请求头
	 * @param encode 是否需要 url encode
	 * @return 结果
	 */
	String post(String url, Map<String, String> params, HttpHeader header, boolean encode);
}

2.2. 雏形

既然通用接口已经设计出来了,下一步就是需要选择具体发送 HTTP 的工具包。这一步有 2 个要求,既需要根据常见的 HTTP 工具依赖来自动决定,也需要提供用户自定义的配置的便捷性

/**
 * <p>
 * 请求工具类
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019/12/24 18:15
 */
@UtilityClass
public class HttpUtil {
	private static Http proxy;

	static {
		Http defaultProxy = null;
		ClassLoader classLoader = HttpUtil.class.getClassLoader();
		// 基于 java 11 HttpClient
		if (ClassUtil.isPresent("java.net.http.HttpClient", classLoader)) {
			defaultProxy = new com.xkcoding.http.support.java11.HttpClientImpl();
		}
		// 基于 okhttp3
		else if (ClassUtil.isPresent("okhttp3.OkHttpClient", classLoader)) {
			defaultProxy = new OkHttp3Impl();
		}
		// 基于 httpclient
		else if (ClassUtil.isPresent("org.apache.http.impl.client.HttpClients", classLoader)) {
			defaultProxy = new HttpClientImpl();
		}
		// 基于 hutool
		else if (ClassUtil.isPresent("cn.hutool.http.HttpRequest", classLoader)) {
			defaultProxy = new HutoolImpl();
		}
		proxy = defaultProxy;
	}

	public void setHttp(Http http) {
		proxy = http;
	}

	private void checkHttpNotNull(Http proxy) {
		if (null == proxy) {
			throw new SimpleHttpException("HTTP 实现类未指定!");
		}
	}

	/**
	 * GET 请求
	 *
	 * @param url URL
	 * @return 结果
	 */
	public String get(String url) {
		checkHttpNotNull(proxy);
		return proxy.get(url);
	}

	/**
	 * GET 请求
	 *
	 * @param url    URL
	 * @param params 参数
	 * @param encode 是否需要 url encode
	 * @return 结果
	 */
	public String get(String url, Map<String, String> params, boolean encode) {
		checkHttpNotNull(proxy);
		return proxy.get(url, params, encode);
	}

	/**
	 * GET 请求
	 *
	 * @param url    URL
	 * @param params 参数
	 * @param header 请求头
	 * @param encode 是否需要 url encode
	 * @return 结果
	 */
	public String get(String url, Map<String, String> params, HttpHeader header, boolean encode) {
		checkHttpNotNull(proxy);
		return proxy.get(url, params, header, encode);
	}

	/**
	 * POST 请求
	 *
	 * @param url URL
	 * @return 结果
	 */
	public String post(String url) {
		checkHttpNotNull(proxy);
		return proxy.post(url);
	}

	/**
	 * POST 请求
	 *
	 * @param url  URL
	 * @param data JSON 参数
	 * @return 结果
	 */
	public String post(String url, String data) {
		checkHttpNotNull(proxy);
		return proxy.post(url, data);
	}

	/**
	 * POST 请求
	 *
	 * @param url    URL
	 * @param data   JSON 参数
	 * @param header 请求头
	 * @return 结果
	 */
	public String post(String url, String data, HttpHeader header) {
		checkHttpNotNull(proxy);
		return proxy.post(url, data, header);
	}

	/**
	 * POST 请求
	 *
	 * @param url    URL
	 * @param params form 参数
	 * @param encode 是否需要 url encode
	 * @return 结果
	 */
	public String post(String url, Map<String, String> params, boolean encode) {
		checkHttpNotNull(proxy);
		return proxy.post(url, params, encode);
	}

	/**
	 * POST 请求
	 *
	 * @param url    URL
	 * @param params form 参数
	 * @param header 请求头
	 * @param encode 是否需要 url encode
	 * @return 结果
	 */
	public String post(String url, Map<String, String> params, HttpHeader header, boolean encode) {
		checkHttpNotNull(proxy);
		return proxy.post(url, params, header, encode);
	}
}

2.3. 实现

如果看了前面的设计模式系列文章的话,应该知道上面其实可以用到 3 种设计模式:代理模式、委派模式、策略模式,没看出来的小伙伴,可以去学习学习哦~ 设计模式传送门👉

simple-http 提供了 4 种默认实现,分别是 JDK11 的 HttpClientOkHttp3Apache 的 HttpClienthutool-http,如果项目中同时存在这几个 HTTP 工具的依赖,那么 simple-http 会根据优先级顺序从左往右,自行选择具体执行 HTTP 请求的工具。源码比较多,就不贴在博客里了,关于这 4 种默认实现的详细代码,请自行前往 GitHub 阅读源码实现,源码传送门👉

3. 使用方式

引入 simple-http 依赖

<dependency>
  <groupId>com.xkcoding.http</groupId>
  <artifactId>simple-http</artifactId>
  <version>1.0</version>
</dependency>

再引用具体实现

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>${okhttp3.version}</version>
</dependency>
<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>${httpclient.version}</version>
</dependency>
<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-http</artifactId>
  <version>${hutool.version}</version>
</dependency>

然后就可以使用 HttpUtil.xxx 开心的耍起来了~

4. 不足与改进

  1. 目前 simple-http 仅支持 GET、POST 方式,其余方式,未来有机会的话,会考虑扩展,目前对 JustAuth 来说,已经够用啦~
  2. 目前返回值都是 String,后续要不要加一个自动解析返回类型的接口?如果响应是 JSON 内容的, 就默认返回 JSON,如果是 html 结构的,就默认返回 Document 等等。哎呀呀,再说吧,再说吧~

5. 其他


编辑页面
分享到:

上一篇
又是一年总结时
下一篇
设计模式之结构型设计模式-装饰者模式