参考:https://blog.csdn.net/chendasheng/article/details/107075468
一、前言
今天看到Spring的RestTemplate底层既可以使用HttpClient,也可以使用OkHttp。不由想到两者之间有什么区别呢,什么情况下选择HttpClient,什么情况下选择OkHttp 。
果然又涉及到我的知识盲区了,所以晚上赶紧补一下课。
HttpClient和OkHttp都是用来远程调用提供http接口的服务
二、HttpClient的使用
参考:https://blog.csdn.net/yangsf_/article/details/124527687
导入依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
操作步骤:
- 创建HttpClient实例
- 创建某种请求方法的实例
- 调用HttpClient实例的execute方法来执行请求方法
- 读取response
- 释放连接,无论执行方法是否成功
1、Get请求
public class GetTest {
public static void main(String[] args) throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://localhost:8082/getName");
CloseableHttpResponse response = httpClient.execute(httpGet);
int status = response.getStatusLine().getStatusCode();
if (status == 200 ) {
HttpEntity entity = response.getEntity();
String res = EntityUtils.toString(entity);
System.out.println("远程调用--->" + res);
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
response.close();
httpClient.close();
}
}

2、post请求
步骤基本上一模一样,只不过可以设置让post请求带上参数,携带json形式的body参数
public class PostTest {
public static void main(String... args) throws IOException {
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("http://localhost:8082/addUser");
User user = User.builder().id(2L).name("houchen").build();
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
httpPost.setEntity(new StringEntity(JSONObject.toJSONString(user), "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpPost);
int status = response.getStatusLine().getStatusCode();
if (status == 200) {
HttpEntity entity = response.getEntity();
String res = EntityUtils.toString(entity);
System.out.println("res-->" + res);
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
response.close();
httpclient.close();
}
}

3、post表单请求
public class FormSubmitTest {
public static void main(String[] args) throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
List<NameValuePair> kv = new ArrayList<>();
kv.add(new BasicNameValuePair("id", "1"));
kv.add(new BasicNameValuePair("name", "zhangsan"));
HttpPost httpPost = new HttpPost("http://localhost:8082/addUser");
httpPost.setEntity(new UrlEncodedFormEntity(kv, Consts.UTF_8));
CloseableHttpResponse response = httpClient.execute(httpPost);
int status = response.getStatusLine().getStatusCode();
if (status == 200) {
HttpEntity entity = response.getEntity();
String res = EntityUtils.toString(entity);
System.out.println("res-->" + res);
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
response.close();
httpClient.close();
}
}

三、OkHttp的使用
参考:https://blog.csdn.net/qq_43842093/article/details/121877245
导入依赖
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.3.1</version>
</dependency>
操作步骤:
- 创建OkHttpClient对象
- 创建Request对象
- 将Request 对象封装为Call
- 通过Call 来执行同步或异步请求,调用execute方法同步执行,调用enqueue方法异步执行
1、Get请求
public class OkHttpGetTest {
public static void main(String[] args) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://localhost:8082/getName")
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
String s = new String(response.body().bytes());
System.out.println(s);
}
}

- client执行newCall方法会得到一个Call对象,表示一个新的网络请求
- Call对象的execute方法是同步方法,会阻塞当前线程,其返回Response对象
- 通过Response对象的isSuccessful()方法可以判断请求是否成功
2、异步Get请求
@Slf4j
public class OkHttpAsyncGetTest {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://localhost:8082/getName")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
String s = new String(response.body().bytes());
log.info("res--->:{}", s);
log.info("异步请求执行完毕!!!");
}
});
log.info("主线程执行完毕!!!");
}
}

- 要想异步执行网络请求,需要执行Call对象的enqueue方法,enqueue方法不会阻塞当前线程,会新开一个工作线程,让实际的网络请求在工作线程中执行。一般情况下这个工作线程的名字以“Okhttp”开头,并包含连接的host信息,比如上面例子中的工作线程的name是"http://localhost:8082/getName"
- 当异步请求成功后,会回调Callback对象的onResponse方法,在该方法中可以获取Response对象。当异步请求失败或者调用了Call对象的cancel方法时,会回调Callback对象的onFailure方法。onResponse和onFailure这两个方法都是在工作线程中执行的
补充:请求头和响应头
响应头: 通过Headers.get(headerName)方法也可以获取某个响应头的值,比如通过headers.get(“Content-Type”)获得服务器返回给客户端的数据类型。 但是服务器返回给客户端的响应头中有可能有多个重复名称的响应头,比如在某个请求中,服务器要向客户端设置多个Cookie,那么会写入多个Set-Cookie响应头,且这些Set-Cookie响应头的值是不同的,访问百度首页,可以看到有7个Set-Cookie的响应头,如下图所示:  为了解决同时获取多个name相同的响应头的值,Headers中提供了一个public List values(String name)方法,该方法会返回一个List对象,所以此处通过Headers对象的values(‘Set-Cookie’)可以获取全部的Cookie信息,如果调用Headers对象的get(‘Set-Cookie’)方法,那么只会获取最后一条Cookie信息。
请求头: 同样的,也可能同时存在多个名称一样的请求头,调用header(name, value)方法就可以设置请求头的name和value, 调用该方法会确保整个请求头中不会存在多个名称一样的name。如果想添加多个name相同的请求头,应该调用addHeader(name, value)方法,这样可以添加重复name的请求头,其value可以不同
Request request = new Request.Builder()
.url("https://api.github.com/repos/square/okhttp/issues")
.header("User-Agent", "OkHttp Headers.java")
.addHeader("Accept", "application/json; q=0.5")
.addHeader("Accept", "application/vnd.github.v3+json")
.build();
3、post发送String请求
@Slf4j
public class OkHttpPostTest {
public static void main(String[] args) throws IOException {
OkHttpClient client = new OkHttpClient();
User user = User.builder().id(2L).name("houchen").build();
String str = JSONObject.toJSONString(user);
Request request = new Request.Builder()
.url("http://localhost:8082/addUser")
.post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), str))
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
log.info(response.body().string());
}
}
4、设置超时
次HTTP请求实际上可以分为三步:
- 客户端与服务器建立连接
- 客户端发送请求数据到服务器,即数据上传
- 服务器将响应数据发送给客户端,即数据下载
由于网络、服务器等各种原因,这三步中的每一步都有可能耗费很长时间,导致整个HTTP请求花费很长时间或不能完成。 为此,可以通过OkHttpClient.Builder的connectTimeout()方法设置客户端和服务器建立连接的超时时间, 通过writeTimeout()方法设置客户端上传数据到服务器的超时时间, 通过readTimeout()方法设置客户端从服务器下载响应数据的超时时间。
在2.5.0版本之前,Okhttp默认不设置任何的超时时间,从2.5.0版本开始,Okhttp将连接超时、写入超时(上传数据)、读取超时(下载数据)的超时时间都默认设置为10秒。如果HTTP请求需要更长时间,那么需要我们手动设置超时时间。
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
5、ResponseBody
四、区别
- OkHttp创建客户端的方式简单一点
- OkHttp使用.post/.delete/.put/.get方法表示请求类型;HttpClient创建HttpGet、HttpPost等这些方法来创建请求类型
- 如果HttpClient需要发送异步请求,需要额外的引入异步请求依赖
- 单例模式下,HttpClient的响应速度要更快一些;非单例模式下,OkHttp的性能更好
|