在做网络编程时,很多程序猿都会使用Apache的HttpClient,也有很多大佬自己封装HttpURLConnection,但是笔者认为最强的,还是大名鼎鼎的OkHttp。
源码地址:https://github.com/square/okhttp
使用方法:参照这位老铁的博客,写的非常详细。
https://blog.csdn.net/tanga842428/article/details/78713089
客户端使用可以使用单例模式:
https://blog.csdn.net/weixin_40281743/article/details/85330193
注意一点:maven导入依赖时,建议排除安卓的依赖。
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.11.0</version>
<exclusions>
<exclusion>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
</exclusion>
</exclusions>
</dependency>
另外:设置超时与超时异常处理:
https://blog.csdn.net/do168/article/details/51848895
okhttp使用完后需要关闭流,释放资源吗?
https://juejin.im/post/5a524eef518825732c536025
OKHttp官方推荐使用单例模式创建client,简单的封装一个工具类
public class HttpUtil {
private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);
private static OkHttpClient client;
private static final String DEFAULT_MEDIA_TYPE = "application/json; charset=utf-8";
private static final int CONNECT_TIMEOUT = 5;
private static final int READ_TIMEOUT = 7;
private static final String GET = "GET";
private static final String POST = "POST";
/**
* 单例模式 获取类实例
*
* @return client
*/
private static OkHttpClient getInstance() {
if (client == null) {
synchronized (OkHttpClient.class) {
if (client == null) {
client = new OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build();
}
}
}
return client;
}
public static String doGet(String url, String callMethod) throws HttpStatusException {
try {
long startTime = System.currentTimeMillis();
addRequestLog(GET, callMethod, url, null, null);
Request request = new Request.Builder().url(url).build();
// 创建一个请求
Response response = getInstance().newCall(request).execute();
int httpCode = response.code();
String result;
ResponseBody body = response.body();
if (body != null) {
result = body.string();
addResponseLog(httpCode, result, startTime);
} else {
response.close();
throw new RuntimeException("exception in OkHttpUtil,response body is null");
}
return handleHttpResponse(httpCode, result);
} catch (Exception ex) {
handleHttpThrowable(ex, url);
return StringUtils.EMPTY;
}
}
public static String doPost(String url, String postBody, String mediaType, String callMethod) throws HttpStatusException {
try {
long startTime = System.currentTimeMillis();
addRequestLog(POST, callMethod, url, postBody, null);
MediaType createMediaType = MediaType.parse(mediaType == null ? DEFAULT_MEDIA_TYPE : mediaType);
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(createMediaType, postBody))
.build();
Response response = getInstance().newCall(request).execute();
int httpCode = response.code();
String result;
ResponseBody body = response.body();
if (body != null) {
result = body.string();
addResponseLog(httpCode, result, startTime);
} else {
response.close();
throw new IOException("exception in OkHttpUtil,response body is null");
}
return handleHttpResponse(httpCode, result);
} catch (Exception ex) {
handleHttpThrowable(ex, url);
return StringUtils.EMPTY;
}
}
public static String doPost(String url, Map<String, String> parameterMap, String callMethod) throws HttpStatusException {
try {
long startTime = System.currentTimeMillis();
List<String> parameterList = new ArrayList<>();
FormBody.Builder builder = new FormBody.Builder();
if (parameterMap.size() > 0) {
for (Map.Entry<String, String> entry : parameterMap.entrySet()) {
String parameterName = entry.getKey();
String value = entry.getValue();
builder.add(parameterName, value);
parameterList.add(parameterName + ":" + value);
}
}
addRequestLog(POST, callMethod, url, null, Arrays.toString(parameterList.toArray()));
FormBody formBody = builder.build();
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
Response response = getInstance().newCall(request).execute();
String result;
int httpCode = response.code();
ResponseBody body = response.body();
if (body != null) {
result = body.string();
addResponseLog(httpCode, result, startTime);
} else {
response.close();
throw new IOException("exception in OkHttpUtil,response body is null");
}
return handleHttpResponse(httpCode, result);
} catch (Exception ex) {
handleHttpThrowable(ex, url);
return StringUtils.EMPTY;
}
}
private static void addRequestLog(String method, String callMethod, String url, String body, String formParam) {
logger.info("===========================request begin================================================");
logger.info("URI : {}", url);
logger.info("Method : {}", method);
if (StringUtils.isNotBlank(body)) {
logger.info("Request body : {}", body);
}
if (StringUtils.isNotBlank(formParam)) {
logger.info("Request param: {}", formParam);
}
logger.info("---------------------------request end--------------------------------------------------");
}
private static void addResponseLog(int httpCode, String result, long startTime) {
long endTime = System.currentTimeMillis();
logger.info("Status : {}", httpCode);
logger.info("Response : {}", result);
logger.info("Time : {} ms", endTime - startTime);
logger.info("===========================response end================================================");
}
private static String handleHttpResponse(int httpCode, String result) throws HttpStatusException {
if (httpCode == HttpStatus.OK.value()) {
return result;
}
HttpStatus httpStatus = HttpStatus.valueOf(httpCode);
throw new HttpStatusException(httpStatus);
}
private static void handleHttpThrowable(Exception ex, String url) throws HttpStatusException {
if (ex instanceof HttpStatusException) {
throw (HttpStatusException) ex;
}
logger.error("OkHttp error url: " + url, ex);
if (ex instanceof SocketTimeoutException) {
throw new RuntimeException("request time out of OkHttp when do url:" + url);
}
throw new RuntimeException(ex);
}
}
public class HttpStatusException extends Exception {
private HttpStatus httpStatus;
private String result;
public HttpStatusException(HttpStatus httpStatus) {
this.httpStatus = httpStatus;
}
public HttpStatusException(HttpStatus httpStatus, String result) {
this.httpStatus = httpStatus;
}
public HttpStatus getHttpStatus() {
return httpStatus;
}
public String getResult() {
return result;
}
}
public class JsonUtil {
private JsonUtil() {
}
/**
* 避免指令重排序导致拿到半初始化的对象,保证线程可见性
*/
private static volatile Gson gson;
private static Gson getGson() {
if (gson == null) {
synchronized (JsonUtil.class) {
if (gson == null) {
gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
}
}
}
return gson;
}
public static <T> String toJson(T object) {
return getGson().toJson(object);
}
public static <T> T fromJson(String json, Type type) {
return getGson().fromJson(json, type);
}
public static <T> T fromJson(String json, Class<T> clazz) {
return getGson().fromJson(json, clazz);
}
访问https
如果遇到:
java.lang.Exception: sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
这个异常是由于您在使用OkHttp库时遇到了证书验证问题导致的。
在HTTPS通信中,服务器会使用SSL证书来验证其身份。客户端(okhttputil)需要验证服务器的证书是否有效,以确保您正在与合法的服务器进行通信,而不是中间人攻击者。
验证失败,可能有以下几个原因:
-
证书链问题:服务器证书可能没有正确设置整个证书链,或者中间证书(Intermediate Certificate)缺失。客户端需要完整的证书链才能验证服务器的证书。
-
证书过期:服务器证书的有效期已过,客户端不再信任过期的证书。
-
自签名证书:如果服务器使用自签名证书而不是受信任的公共CA(证书颁发机构)签署的证书,客户端可能会拒绝验证。
为了解决这个问题,有几种方法可以尝试:
- 信任所有证书(不推荐):这不是一个安全的解决方案,但可以在测试或临时情况下使用。通过OkHttp配置一个信任所有证书的TrustManager,但请不要在生产环境中使用该方法,因为它会绕过证书验证,存在安全风险。
private static final class ClientHolder {
static final OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(getUnsafeSSLSocketFactory(), getUnsafeTrustManager())
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build();
}
private static SSLSocketFactory getUnsafeSSLSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new X509TrustManager[]{getUnsafeTrustManager()}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException("Failed to create unsafe SSLSocketFactory", e);
}
}
private static X509TrustManager getUnsafeTrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
-
添加缺失的中间证书:如果服务器证书缺少中间证书,将缺失的中间证书添加到服务器配置中,以便客户端能够正确验证证书链。
-
使用受信任的证书:确保服务器证书来自受信任的CA,如果是公共网站,一般情况下应该是这样。
-
自定义信任策略:实现自己的TrustManager来验证服务器证书,可以在其中定义您自己的验证逻辑,但请谨慎使用,确保安全性。