有疑必看
功能支持
支持 SSL(HTTPS)吗?
答:支持,比如以下请求百度的带 https 的网址,不需要任何配置就可以正常运行:
HTTP http = HTTP.builder().build();
String baidu = http.sync("https://www.baidu.com")
.get()
.getBody()
.toString();
System.out.println(baidu);
2
3
4
5
6
当然这有一个前提就是是服务器配置的 SSL 证书是值得信任并且有效的,这也是我们推荐的一种方式。
如果服务器的 SSL 证书不是在权威机构购买而是自己生成的(不推荐这种做法),则需要配置sslSocketFactory
和hostnameVerifier
即可:
HTTP http = HTTP.builder()
.config(b -> {
b.sslSocketFactory(mySSLSocketFactory, myTrustManager);
b.hostnameVerifier(myHostnameVerifier);
})
.build();
2
3
4
5
6
例如,让 OkHttps 信任所有,上述代码中的mySSLSocketFactory
、myTrustManager
和myHostnameVerifier
可通过如下方式生成:
X509TrustManager myTrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
HostnameVerifier myHostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(null, new TrustManager[] { myTrustManager }, new SecureRandom());
SSLSocketFactory mySSLSocketFactory = sslCtx.getSocketFactory();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
支持 Cookie 吗?
答:支持,配置方和 OkHttp 完全一样,只需要配置一个 CookieJar 即可:
CookieJar myCookieJar = new CookieJar() {
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
// TODO: 保存 cookies
}
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
// TODO: 读取 cookies
return null;
}
};
HTTP http = HTTP.builder()
.config(b -> {
b.cookieJar(myCookieJar);
})
.build();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
以上配置后,OkHttps 便具有了自动管理 Cookie 的功能,具体请求用户便不用操心 Cookie 了,但如果想手动自己添加一个 Cookie 的话,那只需要添加一个请求头即可,如下:
http.async("https://...")
// 添加两个 Cookie
.addHeader("Cookie", "cname1=cvalue1; cname2=cvalue2")
// ...
.post();
2
3
4
5
支持代理(Proxy)吗?
答:支持,只需配置 Proxy 即可,例如:
HTTP http = HTTP.builder()
.config(b -> {
b.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("www.your-proxy.com", 8080)));
})
.build();
2
3
4
5
支持缓存(Cache)吗?
答:支持,只需配置 Cache 即可,例如:
HTTP http = HTTP.builder()
.config(b -> {
b.cache(new Cache("/path-to-cache", 10 * 1024 * 1024));
})
.build();
2
3
4
5
有失败重试机制吗?
答:很简单,比如以下配置就可实现请求超时重试三次:
HTTP http = HTTP.builder()
.config(b -> {
b.addInterceptor(chain -> {
int retryTimes = 0;
while (true) {
try {
return chain.proceed(chain.request());
} catch (SocketTimeoutException e) {
if (retryTimes >= 3) {
throw e;
}
System.out.println("超时重试第" + retryTimes + "次!");
retryTimes++;
}
}
});
}).build();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
以下代码实现服务器状态码为 500 时,自动重试三次:
HTTP http = HTTP.builder()
.config(b -> {
b.addInterceptor(chain -> {
int retryTimes = 0;
while (true) {
Response response = chain.proceed(chain.request());
if (response.code() == 500 && retryTimes < 3) {
System.out.println("失败重试第" + retryTimes + "次!");
// 注意,这里一定要 close 掉失败的 Response
response.close();
retryTimes++;
continue;
}
return response;
}
});
}).build();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
当然也可以把两者结合起来:
HTTP http = HTTP.builder()
.config(b -> {
b.addInterceptor(chain -> {
int retryTimes = 0;
while (true) {
Response response = null;
Exception exception = null;
try {
response = chain.proceed(chain.request());
} catch (Exception e) {
exception = e;
}
if ((exception != null || response.code() == 500) && retryTimes < 3) {
System.out.println("失败重试第" + retryTimes + "次!");
if (response != null) {
// 注意,这里一定要 close 掉失败的 Response
response.close();
}
retryTimes++;
continue;
} else if (exception != null) {
throw exception;
}
return response;
}
});
}).build();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
常见异常
HttpException: 没有匹配[null/json]类型的转换器!
当出现这个异常时,一般是让 OkHttps 去自动解析 JSON 却没有给它配置MsgConvertor
导致的,当遇到这个异常,可按如下步骤检查:
1、 项目依赖中是否添加了 json 扩展包:okhttps-fastjson
、okhttps-gson
、okhttps-jackson
,添加一个即可;
2、 发起请求时,使用的是 OkHttps 提供的工具类(OkHttps
或HttpUtils
)还是 自己构建的HTTP
实例,如果是前者,框架会自动配置MsgConvertor
,若是后者,得自己手动配置MsgConvertor
:
HTTP http = HTTP.builder()
.addMsgConvertor(new GsonMsgConvertor()); // okhttps-gson
.addMsgConvertor(new JacksonMsgConvertor()); // okhttps-jackson
.addMsgConvertor(new FastjsonMsgConvertor()); // okhttps-fastjson
.build();
2
3
4
5
3、 项目依赖中已经添加了 json 扩展包,并且使用的是 OkHttps 提供的工具类(OkHttps
或HttpUtils
),但还是有这个异常(罕见),这个时候一般是 IDE 的编译器的 BUG 导致的,请 clean 一下项目,重新运行即可。
HttpException: 转换失败 Caused by IOException: closed
当出现这个异常时,很可能是对报文体重复消费(多次调用 toXXX 方法)造成的,类似以下代码:
Body body = OkHttps.sync("/api/users/1").get().getBody();
log.info("body = " + body); // 这里隐式的调用了 body 的 toString 消费方法
User user = body.toBean(User.class); // 这里又调用了一次 toBean,将会抛出异常
2
3
4
5
以上代码,由于多次调用报文体的消费方法,则会导致此异常,如果确实需要多次消费时,可以先使用cache
方法,如下:
Body body = OkHttps.sync("/api/users/1").get().getBody()
.cache(); // 先调用 cache 方法,就可以多次消费了
log.info("body = " + body); // 这里隐式的调用了 body 的 toString 消费方法
User user = body.toBean(User.class); // 又调用了一次 toBean,则不会再有问题
Mapper mapper = body.toMapper(); // 再调用一次,依然没问题
2
3
4
5
6
7
HttpException: 报文体转换字符串出错 Caused by IOException: Content-Length (xxx) and stream length (0) disagree
当出现这个异常,同样很可能是多次消费报文体的问题(同上),再类似以下的代码:
HttpResult.Body body1 = OkHttps.async("/api/...")
.setOnResponse(res -> {
HttpResult.Body body2 = res.getBody();
String str2 = body2.toString(); // 这里消费了一次报文体
// ...
})
.get()
.getResult()
.getBody();
String str1 = body1.toString(); // 这里又消费了一次报文体
2
3
4
5
6
7
8
9
10
11
以上的代码,在第 4、11 行都消费了报文体,但是没有提前使用cache()
方法,所以会报错,如下修改即可:
HttpResult.Body body1 = OkHttps.async("/api/...")
.setOnResponse(res -> {
HttpResult.Body body2 = res.getBody()
.cache(); // 使用 cache
String str2 = body2.toString();
// ...
})
.get()
.getResult()
.getBody()
.cache(); // 使用 cache
String str1 = body1.toString();
2
3
4
5
6
7
8
9
10
11
12
13
若使用的 OkHttps 版本是 v2.4.2 及以前版本,上面的代码还得考虑线程安全问题,加一个锁即可:
Object lock = new Object();
HttpResult res1 = OkHttps.async("/api/...")
.setOnResponse(res2 -> {
synchronized(lock) {
String str2 = res2.getBody().cache().toString();
// ...
}
})
.get()
.getResult()
synchronized(lock) {
String str1 = res1.getBody().cache().toString();
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
OkHttps v2.4.3 及以后版本则不必如此。
NoSuchMethodError: kotlin.collections.ArraysKt.copyInto([B[BIII)[B
一般出现这个问题,是因为依赖了 v4.x 的 OkHttp, 而你的项目是纯 Java 项目,没有添加 kotlin 的依赖导致(由于 OkHttp v4.x 是用 kotlin 重写)。
解决办法:依赖换成 v3.x 的 OkHttp 即可。
JSON 请求后端收不到数据,JSON 被加上双引号当做字符串了?
List<String> values = new ArrayList<>();
values.add("value1");
values.add("value2");
OkHttps.sync("/api/...")
.bodyType(OkHttps.JSON)
.addBodyPara("name", "Test")
.addBodyPara("values", values)
.post();
2
3
4
5
6
7
8
9
如上,用户可能期望发送这样的 JSON 给服务器:
{
"name": "Test",
"values": [ "value1", "value2" ]
}
2
3
4
但实际上服务器收到的却是这样:
{
"name": "Test",
"values": "[\"value1\", \"value2\"]"
}
2
3
4
这是因为addBodyPara
方法添加的参数只支持单层数据结构,若要支持多层数据结构,必须使用setBodyPara
方法,如下:
List<String> values = new ArrayList<>();
values.add("value1");
values.add("value2");
Map<String, Object> paraMap = new HashMap<>();
paraMap.put("name", "Test");
paraMap.put("values", values);
OkHttps.sync("/api/...")
.bodyType(OkHttps.JSON)
.setBodyPara(paraMap)
.post();
2
3
4
5
6
7
8
9
10
11
12
还有其它问题,怎么解决?
在调试模式中
如果你是在调式模式下遇到问题,那可以先参考 调式注意事项 章节。
查看已有 ISSUE
到 GitHub 的 issue 里看看有没有人提过类似的问题:https://github.com/ejlchina/okhttps/issues?q=is%3Aissue+is%3Aclosed
到 Gitee 的 issue 里看看有没有人提过类似的问题:https://gitee.com/troyzhxu/okhttps/issues?assignee_id=&author_id=&branch=&issue_search=&label_name=&milestone_id=&program_id=&scope=&sort=&state=closed
利用搜索引擎
例如当遇到如下异常时:
检索关键字一定要输入上图中的 Cause by 部分,如下:
这样便很容易检索到问题的答案,其它异常也是类似。
进入有问必答群
若通过以上几步,问题还没有得到解决,可先加微信:18556739726(请备注 OkHttps)再入群交流,有问必答!!!。