okhttp3简单使用
文章目录一、简介1、特性二、导入依赖三、创建 get 请求四、创建 post 请求五、创建异步请求(MultipartBody 表单)六、FormBody 表单参数七、OkhttpConfig 配置类八、OkhttpUtil 工具类九、小结一、简介Square 公司开源的 OkHttpokhttp 官网 https://square.github.io/okhttp/1、特性1)支持HTTP2,对
文章目录
一、简介
Square 公司开源的 OkHttp
okhttp 官网 https://square.github.io/okhttp/
- 网络编程(含 UrlConnection 发送 HTTP 请求下载文件):https://blog.csdn.net/qq_41538097/article/details/118722718
- Apache HttpComponents 简单使用:https://blog.csdn.net/qq_41538097/article/details/123719298
- RestTemplate简单使用:https://blog.csdn.net/qq_41538097/article/details/123560238
- okhttp3简单使用:https://blog.csdn.net/qq_41538097/article/details/123677614
- HttpURLConnection 简单使用:https://blog.csdn.net/qq_41538097/article/details/123697432
1、特性
1)支持HTTP2,对一台机器的所有请求共享同一个socket连接(同域名下所有通信都在单个连接上完成,消除了因多个连接而带来的延时和内存消耗)。
2)内置连接池,支持连接复用,减少请求延迟
3)透明的GZIP压缩减少响应数据的大小
4)通过缓存避免重复请求
5)失败时自动重连,自动重定向
当网络出现问题的时候OkHttp依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP,OkHttp使用现代TLS技术(SNI, ALPN)初始化新的连接,当握手失败时会回退到TLS 1.0。
使用:OkHttp 是一个适用于 Android、Kotlin 和 Java 应用的 HTTP 和 HTTP/2 客户端,它的请求/响应 API 使用构造器模式 builders 来设计,它支持阻塞式的同步请求和带回调的异步请求。
如果你使用 OkHttpClient,你不用重写你的代码,okhttp-urlconnection 模块实现了 java.net.HttpURLConnection 中的API,okhttp-apache 模块实现了 HttpClient 中的API
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
持续更新,和上面的同版本更新
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-urlconnection</artifactId>
<version>4.9.3</version>
</dependency>
2016 停止更新
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp-apache</artifactId>
<version>2.7.5</version>
</dependency>
二、导入依赖
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
三、创建 get 请求
因为是测试,OkHttpClient 是新建的对象,所以不需要配置类,如果需要 bean 工厂可以添加配置类步骤七
,工具类步骤八
@Test
public void getUrlParams() throws IOException {
OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(60, TimeUnit.SECONDS)//设置连接超时时间
.readTimeout(60, TimeUnit.SECONDS)//设置读取超时时间
.build();
String url = "http://127.0.0.1:8080/rest/get/url_path_params?name=admin&id=98";
Request.Builder builder = new Request.Builder();
Request request = builder.url(url).build();
Response response = okHttpClient.newCall(request).execute();
ResponseBody body = response.body();
System.out.println(body.string());
}
四、创建 post 请求
post 请求传递 json 数据 Content-Type:application/json; charset=utf-8
@PostMapping("/post/params_json_list")
public JSONObject postParamsJsonList(@RequestBody List<Map<String, Object>> list) {
JSONObject jsonObject = JSON.parseObject("{\"message\":\"SUCCESS\",\"code\":200,\"type\":\"okhttp3\"}");
jsonObject.put("body", list);
return jsonObject;
}
RequestBody 封装 body 参数
@Test
public void postParamsJson() throws IOException {
OkHttpClient build = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS).build();
String json = "{\"name\": \"admin\",\"id\": 10}";
String url = "http://127.0.0.1:8080/okhttp3/post/params_json";
RequestBody requestBody = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
Request request = new Request.Builder().post(requestBody).url(url).build();
Response execute = build.newCall(request).execute();
ResponseBody responseBody = execute.body();
assert responseBody != null;
log.info(responseBody.string());
}
五、创建异步请求(MultipartBody 表单)
form 表单上传表单(含文件)Content-Type:multipart/form-data;
@PostMapping("/post/params_from_async")
public JSONObject postParamsFromAsync(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws InterruptedException, IOException, ServletException {
TimeUnit.SECONDS.sleep(30);
JSONObject jsonObject;
TreeMap<String, Object> treeMap = new TreeMap<>();
Collection<Part> parts = request.getParts();
parts.forEach((part) -> {
if (part.getContentType() == null) {
treeMap.put(part.getName(), request.getParameter(part.getName()));
}
});
String s = UUID.randomUUID().toString().replace("-", "").substring(0, 5) + Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().indexOf("."));
try {
file.transferTo(new File("I:\\spring\\spring-boot-aop-test\\file\\" + s));
jsonObject = JSON.parseObject("{\"message\":\"文件下载成功\",\"code\":200,\"type\":\"okhttp3\"}");
jsonObject.put("body", treeMap);
} catch (IOException e) {
e.printStackTrace();
jsonObject = JSON.parseObject("{\"message\":\"文件下载失败\",\"code\":201,\"type\":\"okhttp3\"}");
}
return jsonObject;
}
测试方法
@Test
public void postParamsFromAsync() throws IOException {
String url = "http://127.0.0.1:8080/okhttp3/post/params_from_async";
File file = new File("C:\\Users\\Administrator\\Pictures\\图标\\influxdb-studio.png");
RequestBody multipartBody = MultipartBody.create(file, MediaType.parse("image/png"));
MultipartBody body = new MultipartBody.Builder()
.addFormDataPart("file", "influxdb.png", multipartBody)
.addFormDataPart("age", "18")
.build();
Request request = new Request.Builder()
.post(body)
.url(url)
.addHeader("Connection", "keep-alive")
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
log.error("okhttp", "onFailure: " + e.getMessage());
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
log.info("okhttp", "onResponse: " + response.body().string());
}
});
}
六、FormBody 表单参数
form 表单上传表单 Content-Type:application/x-www-form-urlencoded
@PostMapping("/post/params_from")
public JSONObject postParamsFrom(HttpServletRequest request) throws InterruptedException, IOException, ServletException {
JSONObject jsonObject;
Map<String, Object> treeMap = new TreeMap<>();
Map<String, String[]> parameterMap = request.getParameterMap();
parameterMap.forEach((key, value) -> {
if (value.length == 1) {
treeMap.put(key, value[0]);
} else {
treeMap.put(key, value);
}
});
jsonObject = JSON.parseObject("{\"message\":\"表单读取成功\",\"code\":200,\"type\":\"okhttp3\"}");
if (treeMap.keySet().size() > 0) {
jsonObject.put("body", treeMap);
}
return jsonObject;
}
测试方法
@Autowired
OkHttpClient okHttpClient;
@Test
public void postParamsFrom() throws IOException {
String url = "http://127.0.0.1:8080/okhttp3/post/params_from";
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("password", "123456");
linkedHashMap.put("address", "西安");
FormBody.Builder builder = new FormBody.Builder();
builder.add("name", "admin");
linkedHashMap.forEach((key, value) -> {
builder.add(key, value);
});
RequestBody formBody = builder.build();
Request request = new Request.Builder()
.post(formBody)
.url(url)
.addHeader("Connection", "keep-alive")
.build();
Response response = okHttpClient.newCall(request).execute();
log.info(response.body().string());
}
七、OkhttpConfig 配置类
详细配置参考 https://www.freesion.com/article/3815946206/
@Configuration
@Slf4j
public class OkHttpConfig {
@Bean
public OkHttpClient okHttpClient() throws NoSuchAlgorithmException, KeyStoreException {
// 详细配置可参考 https://www.freesion.com/article/3815946206/
return new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS) //设置连接超时
.readTimeout(60, TimeUnit.SECONDS) //设置读超时
.writeTimeout(60, TimeUnit.SECONDS) //设置写超时
.retryOnConnectionFailure(true) //是否自动重连
// 设置https配置,此处忽略了所有证书
.sslSocketFactory(sslSocketFactory(), new EasyX509TrustManager(null))
// 验证服务器的证书域名。在https握手期间,如果 URL 的主机名和服务器的标识主机名不匹配,则验证机制可以回调此接口的实现程序来确定是否应该允许此连接
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
})
// 拦截器日志记录
.addInterceptor((Interceptor.Chain chain) -> {
Request request = chain.request();
log.info("intercept 接口地址:" + request.url()
+ "\r\n接口参数):" + request.body().toString());
return chain.proceed(chain.request());
})
// 添加证书,一般不要设置
.certificatePinner(new CertificatePinner.Builder()
.add("test1.com", "sha1/sdfsdsdsdsdsdsdsdsdsdsdds=")
.add("test2.com", "sha1/sdsdsdsdsdsdsdsdsdssdssds=")
.build())
//.connectionSpecs(...) // 设置连接的规格、TLS版本和密码套件等,最好不要去设置
//.socketFactory(new SocketFactory() {...}) // 使用定制的用于http请求的套接字
/* .authenticator(new Authenticator() { // 添加授权证书
@Override
public Request authenticate(Route route, Response response) throws IOException {
}
}) */
.cache(new Cache(new File("cache.tmp"), 10 * 1024 * 1024)) // 10M缓存
// 定义连接池,最多有五个空闲连接,每个空闲连接最多保持6分钟
.connectionPool(new ConnectionPool(5, 6, TimeUnit.MINUTES))
// 指定分发器,即异步执行http请求时的线程池,http响应的回调也是在此线程池的线程中执行
// .dispatcher(new Dispatcher(new ThreadPoolExecutor(...)))
.followRedirects(true) // 允许http重定向
.followSslRedirects(false) // 截断https的重定向
.pingInterval(30, TimeUnit.SECONDS) // 设置ping检测网络连通性的间隔。默认为0
//.proxy(...) // 设置单个代理
//.proxyAuthenticator(...) // 设置代理验证
//.proxySelector(...) // 为不同的链接设置不同的代理
.build();
}
public class EasyX509TrustManager implements X509TrustManager {
private X509TrustManager standardTrustManager = null;
/**
* Constructor for EasyX509TrustManager.
*/
public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException,
KeyStoreException {
super();
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory
.getDefaultAlgorithm());
factory.init(keystore);
TrustManager[] trustmanagers = factory.getTrustManagers();
if (trustmanagers.length == 0) {
throw new NoSuchAlgorithmException("no trust manager found");
}
this.standardTrustManager = (X509TrustManager) trustmanagers[0];
}
/**
* @see X509TrustManager#checkClientTrusted(X509Certificate[],
* String authType)
*/
public void checkClientTrusted(X509Certificate[] certificates, String authType)
throws CertificateException {
standardTrustManager.checkClientTrusted(certificates, authType);
}
/**
* @see X509TrustManager#checkServerTrusted(X509Certificate[],
* String authType)
*/
public void checkServerTrusted(X509Certificate[] certificates, String authType)
throws CertificateException {
if ((certificates != null) && (certificates.length == 1)) {
certificates[0].checkValidity();
} else {
standardTrustManager.checkServerTrusted(certificates, authType);
}
}
/**
* @see X509TrustManager#getAcceptedIssuers()
*/
public X509Certificate[] getAcceptedIssuers() {
return this.standardTrustManager.getAcceptedIssuers();
}
}
public SSLSocketFactory sslSocketFactory() {
try {
//信任任何链接
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new EasyX509TrustManager(null)}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
return null;
}
}
八、OkhttpUtil 工具类
@Slf4j
@Component
public class OkHttpUtil{
private static OkHttpClient okHttpClient;
@Autowired
public OkHttpUtil(OkHttpClient okHttpClient) {
OkHttpUtil.okHttpClient= okHttpClient;
}
/**
* get
* @param url 请求的url
* @param queries 请求的参数,在浏览器?后面的数据,没有可以传null
*/
public static String get(String url, Map<String, String> queries) {
String responseBody = "";
StringBuffer sb = new StringBuffer(url);
if (queries != null && queries.keySet().size() > 0) {
boolean firstFlag = true;
Iterator iterator = queries.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry<String, String>) iterator.next();
if (firstFlag) {
sb.append("?" + entry.getKey() + "=" + entry.getValue());
firstFlag = false;
} else {
sb.append("&" + entry.getKey() + "=" + entry.getValue());
}
}
}
Request request = new Request.Builder()
.url(sb.toString())
.build();
Response response = null;
try {
response = okHttpClient.newCall(request).execute();
int status = response.code();
if (response.isSuccessful()) {
return response.body().string();
}
} catch (Exception e) {
log.error("okhttp3 put error >> ", e);
} finally {
if (response != null) {
response.close();
}
}
return responseBody;
}
/**
* post
*
* @param url 请求的url
* @param params post form 提交的参数
* @return
*/
public static String post(String url, Map<String, String> params) {
String responseBody = "";
FormBody.Builder builder = new FormBody.Builder();
//添加参数
if (params != null && params.keySet().size() > 0) {
for (String key : params.keySet()) {
builder.add(key, params.get(key));
}
}
Request request = new Request.Builder()
.url(url)
.post(builder.build())
.build();
Response response = null;
try {
response = okHttpClient.newCall(request).execute();
int status = response.code();
if (response.isSuccessful()) {
return response.body().string();
}
} catch (Exception e) {
log.error("okhttp3 post error >> ", e);
} finally {
if (response != null) {
response.close();
}
}
return responseBody;
}
/**
* get
* @param url 请求的url
* @param queries 请求的参数,在浏览器?后面的数据,没有可以传null
* @return
*/
public static String getForHeader(String url, Map<String, String> queries) {
String responseBody = "";
StringBuffer sb = new StringBuffer(url);
if (queries != null && queries.keySet().size() > 0) {
boolean firstFlag = true;
Iterator iterator = queries.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry<String, String>) iterator.next();
if (firstFlag) {
sb.append("?" + entry.getKey() + "=" + entry.getValue());
firstFlag = false;
} else {
sb.append("&" + entry.getKey() + "=" + entry.getValue());
}
}
}
Request request = new Request.Builder()
.addHeader("key", "value")
.url(sb.toString())
.build();
Response response = null;
try {
response = okHttpClient.newCall(request).execute();
int status = response.code();
if (response.isSuccessful()) {
return response.body().string();
}
} catch (Exception e) {
log.error("okhttp3 put error >> ", e);
} finally {
if (response != null) {
response.close();
}
}
return responseBody;
}
/**
* Post请求发送JSON数据....{"name":"zhangsan","pwd":"123456"}
* 参数一:请求Url
* 参数二:请求的JSON
* 参数三:请求回调
*/
public static String postJsonParams(String url, String jsonParams) {
String responseBody = "";
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
Response response = null;
try {
response = okHttpClient.newCall(request).execute();
int status = response.code();
if (response.isSuccessful()) {
return response.body().string();
}
} catch (Exception e) {
log.error("okhttp3 post error >> ", e);
} finally {
if (response != null) {
response.close();
}
}
return responseBody;
}
/**
* Post请求发送xml数据....
* 参数一:请求Url
* 参数二:请求的xmlString
* 参数三:请求回调
*/
public static String postXmlParams(String url, String xml) {
String responseBody = "";
RequestBody requestBody = RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), xml);
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
Response response = null;
try {
response = okHttpClient.newCall(request).execute();
int status = response.code();
if (response.isSuccessful()) {
return response.body().string();
}
} catch (Exception e) {
log.error("okhttp3 post error >> "+ e);
} finally {
if (response != null) {
response.close();
}
}
return responseBody;
}
}
九、小结
okhttp 创建 request 请求常见的 body 类型
- RequestBody 适合传一个参数,如:json (2 和 3 示例)
- FormBody 表单参数(仅支持文本) (示例 6)
- MultipartBody 表单(支持文本 和 文件)(示例 4)
创建 okhttp 请求,主要生成 request(包含 method、url、body、header),最后通过 OkHttpClient 执行同步(execute)异步(enqueue)方法
Request request = new Request.Builder()
.post(formBody)
.url(url)
.addHeader("Connection", "keep-alive")
.build();
Response response = okHttpClient.newCall(request).execute();
1、OkHttp3使用起来简单,但是实际上其原理代码十分复杂,这也使得OkHttp3功能很强大,包括缓存、gzip、长连接、重定向等。
2、OkHttp3原理复杂,但是它的主要功能就是网络访问,网络访问就是要完成读写功能,于是我们只需要好好把握HttpEngine中的sendRequest作用和readResponse作用即可,即sendRequest完成初始化,并通过Socket方式建立同服务器的网络连接,而readResponse通过Socket完成读写功能。
3、OkHttp3依赖于Okio,主要体现在网络访问数据放在okio.Buffer中,通过okio.Okio write方法写服务器数据,通过okio.Okio read方法读取服务器返回数据。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)