赞
踩
Square 公司开源的 OkHttp
okhttp 官网 https://square.github.io/okhttp/
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>
因为是测试,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 请求传递 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());
}
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()); } }); }
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()); }
详细配置参考 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; } }
@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 类型
创建 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方法读取服务器返回数据。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。