https://github.com/square/okhttp
https://square.github.io/okhttp/
https://stackoverflow.com/questions/tagged/okhttp?sort=active
https://springboot.io/t/topic/154
关于 okhttp 版本问题
在项目中引入 okhttp 版本的选择很关键,因为 okhttp 版本 v4.0.0 是一个分水岭。
在 v4.0.0 之前的版本使用的是 Java 语言编写,在引入 pom 依赖时直接按照如下方式即可:
<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>${okhttp.version}</version></dependency>
但是自从 v4.0.0 开始,okhttp 使用 kotlin 语言进行了重写,所以在引入时防止编译出错需要引入相应的 **kotlin-stdlib**:
<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>${okhttp.version}</version></dependency><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib</artifactId><version>${kotlin-stdlib.version}</version></dependency>
如果不引入 kotlin-stdlib 的话,在编译时可会遇到如下编译错误:
Exception in thread "main" java.lang.NoSuchMethodError: kotlin.collections.ArraysKt.copyInto([B[BIII)[Bat okio.Segment.writeTo(Segment.kt:169)at okio.Segment.compact(Segment.kt:152)at okio.Buffer.write(Buffer.kt:1842)at okio.Buffer.read(Buffer.kt:1854)at okio.Buffer.writeAll(Buffer.kt:1642)at okio.Options$Companion.buildTrieRecursive(Options.kt:187)at okio.Options$Companion.buildTrieRecursive(Options.kt:174)at okio.Options$Companion.buildTrieRecursive$default(Options.kt:113)at okio.Options$Companion.of(Options.kt:72)at okhttp3.internal.Util.<clinit>(Util.kt:71)at okhttp3.OkHttpClient.<clinit>(OkHttpClient.kt:1073)at com.ituknown.okhttp.HttpClient.doPost(HttpClient.java:39)at com.ituknown.okhttp.HttpClient.main(HttpClient.java:13)
FAQ:如何引入相应 okhttp 对应的 **kotlin-stdlib** 版本?
最简单地方式是先将 okhttp 引入到项目中,然后点击 okhttp 对应的 pom 就能看到对应的 kotlin-stdlib 版本了,示例:
创建 OkHttpClient 实例
构造 OkHttpClient 实例:
OkHttpClient okHttpClient = new OkHttpClient();
推荐使用下面的方式:
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().build();
因为在构造实例是我们通常会做些请求设置,比如超时时间、网络拦截器和DNS等。使用 Builder 模式更加方便,实例:
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().connectTimeout(3, TimeUnit.SECONDS) // 设置超时时间.addInterceptor(...) // 添加拦截器.dns(...) // 设置DNS.build();
当然,使用第一种也是可以的。但是你不觉得 Builder 模式阅读起来更爽 :) ?
构造请求数据
OkHttpClient 在执行时,都是需要设置一个 okhttp3.Request 对象。这个 Request 主要用于设置请求类型、请求头、请求地址等等。
示例:
// GET 请求Request request = new Request.Builder().url("...").addHeader("...", "...").get().build();// POST 请求Request request = new Request.Builder().url("...").addHeader("...", "...").post(...).build();// DELETE 请求Request request = new Request.Builder().url("...").addHeader("...", "...").delete().build();// PUT 请求Request request = new Request.Builder().url("...").addHeader("...", "...").put(...).build();
之后使用 OkHttpClient 实例执行即可:
Response response = okHttpClient.newCall(request).execute();
GET 请求
GET 请求比较简单,只需要创建一个 Request 对象即可,实例:
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().readTimeout(1, TimeUnit.SECONDS).build();Request request = new Request.Builder().url("http://localhost:8080/user?id=1").addHeader("Accept", "application/json").get().build();// 同步执行Response response = okHttpClient.newCall(request).execute();int code = response.code(); // 状态码Protocol protocol = response.protocol(); // 请求协议ResponseBody body = response.body(); // 响应体对象String data = body.string(); // 响应数据
注意响应数据是 **body.string()** 而不是 **body.toString()** !!!!
POST 请求
POST 请求相对于 GET 请求唯一多的一步就是构造请求体数据:
Request request = new Request.Builder().url("http://localhost:8080/").addHeader("Accept", "application/json").post(RequestBody) // 注意这里.build();
okhttp3.RequestBody 类内部提供了多种静态方法,用于创建请求体数据的形式:
public static RequestBody create(final MediaType contentType, final byte[] content);public static RequestBody create(final MediaType contentType, final byte[] content, final int offset, final int byteCount);public static RequestBody create(final MediaType contentType, final ByteString content);public static RequestBody create(final MediaType contentType, final byte[] content);public static RequestBody create(final MediaType contentType, final byte[] content, final int offset, final int byteCount);public static RequestBody create(final MediaType contentType, final File file);
其中 okhttp3.MediaType 对象是用于设置请求体类型,比如我想要请求的是 JSON 格式请求体就可以使用如下形式:
String jsonTxt = "...";RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), jsonTxt);
之后设置到 okhttp3.Request 对象即可:
Request request = new Request.Builder().url("http://localhost:8080/").addHeader("Accept", "application/json").post(requestBody) // 注意这里.build();
这些静态方法基本上满足我们日常使用了。不过,okhttp 还提供了两个 okhttp3.RequestBody 的扩展类,分别是用于文件上传的 **okhttp3.MultipartBody** 和 Form 表单数据 **okhttp3.FormBody**:

JSON 请求
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().readTimeout(1, TimeUnit.SECONDS).build();User user = new User();user.setUsername("HanMeimei");user.setAge(18);ObjectMapper objectMapper = new ObjectMapper();String json = objectMapper.writeValueAsString(user);// 创建 JSON 请求体数据RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), json);Request request = new Request.Builder().url("http://localhost:8080/").addHeader("Accept", "application/json").post(requestBody) // 请求体数据.build();// 同步执行Response response = okHttpClient.newCall(request).execute();int code = response.code(); // 状态码Protocol protocol = response.protocol(); // 请求协议ResponseBody body = response.body(); // 响应体对象String data = body.string(); // 响应数据
表单请求
Form 表单请求使用的 Media 类型是 application/x-www-form-urlencoded。okhttp 提供了一个对应的 Form 表单请求体类:FormBody。我们可以直接使用该对象构造 Form 表单数据即可:
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().readTimeout(1, TimeUnit.SECONDS).build();FormBody requestBody = new FormBody.Builder().add("username", "HanMeimei").addEncoded("charset", "UTF-8").build();Request request = new Request.Builder().url("http://localhost:8080/").addHeader("Accept", "application/json").post(requestBody).build();// 同步执行Response response = okHttpClient.newCall(request).execute();int code = response.code(); // 状态码Protocol protocol = response.protocol(); // 请求协议ResponseBody body = response.body(); // 响应体对象String data = body.string(); // 响应数据
需要说明的是,FormBody 对象内部设置了一个默认 MediaType:
private static final MediaType CONTENT_TYPE = MediaType.get("application/x-www-form-urlencoded");
所以我们在发送请求是无需手动设置 MediaType。
文件上传
okhttp 同样提供了一个用于构造文件上传的请求体类:MultipartBody。我们可以直接借助该类构造文件上传对象,示例如下:
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().readTimeout(1, TimeUnit.SECONDS).build();// 文件File file = new File("/path/RequestBody.png");String fileName = file.getName();MultipartBody multipartBody = new MultipartBody.Builder()// 后台接收的 key, 文件名称, 文件对象.addFormDataPart("file", fileName, RequestBody.create(MediaType.parse("image/png"), file)).build();Request request = new Request.Builder().url("http://localhost:8080/upload").addHeader("Accept", "application/json").post(multipartBody).build();// 同步执行Response response = okHttpClient.newCall(request).execute();// 同步执行Response response = okHttpClient.newCall(request).execute();int code = response.code(); // 状态码Protocol protocol = response.protocol(); // 请求协议ResponseBody body = response.body(); // 响应体对象String data = body.string(); // 响应数据
看下后台接收的文件上传示例:
另外,在上面的示例中我没有设置 MediaTye。实际上在 MultipartBody 类内部定义了多个 MediaType 可供我们使用:
public static final MediaType MIXED = MediaType.get("multipart/mixed");public static final MediaType ALTERNATIVE = MediaType.get("multipart/alternative");public static final MediaType DIGEST = MediaType.get("multipart/digest");public static final MediaType PARALLEL = MediaType.get("multipart/parallel");public static final MediaType FORM = MediaType.get("multipart/form-data");
比如我在文件上传时指定以 multipart/form-data 形式进行上传:
MultipartBody multipartBody = new MultipartBody.Builder()// 设置 MediaType.setType(MultipartBody.FORM)// 后台接收的 key, 文件名称, 文件对象.addFormDataPart("file", fileName, RequestBody.create(MediaType.parse("image/png"), file)).build();
文件下载
文件下载就是普通的 GET 请求,唯一的区别就是注意响应数据的处理。进行文件下载时,我们应该获取响应数据的二进制流数据而不是文本字符串,然后再根据具体的媒体进行进行响应的处理即可:
OkHttpClient okHttpClient = new OkHttpClient().newBuilder().readTimeout(1, TimeUnit.SECONDS).build();Request request = new Request.Builder().url("http://localhost:8080/head?id=1").addHeader("Accept", "application/json").get().build();int code = response.code(); // 状态码Protocol protocol = response.protocol(); // 请求协议ResponseBody body = response.body(); // 响应体对象// 注意这里InputStream inputStream = body.byteStream(); // 二进制流数据MediaType mediaType = body.contentType(); // 二进制流的媒体类型
异步请求
上面的示例都是同步请求,即如下形式:
Response response = okHttpClient.newCall(request).execute();
