HTTP
是我们最为熟悉的网络通讯协议,而 OkHttp
则 Java 中流行的网络请求三方库,许多项目产品或多或少都会用到它,如常用的 Retrofit
工具底层即通过 OkHttp
实现,下面就让我们看一下 OkHttp
的基础用法。
一、基本介绍
1. 依赖导入
在 Maven
工程中导入 okhttp3
依赖,为了方便对象序列化同时引入 Jackson
工具。
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
2. 初始化
okhttp
初始化示例如下,在初始化时可指定超时等配置:
public OkHttpClient init() {
return new OkHttpClient.Builder()
// 请求超时时间
.readTimeout(3, TimeUnit.MINUTES)
// 连接超时时间
.connectTimeout(3, TimeUnit.MINUTES)
.build();
}
二、请求头
设置 HTTP
请求头一共有 3
中方式,下面分别进行介绍,需要注意当请求头中无法正常传输中文,需要通过 URLEncode
类库编码后传输。
1. header
通过 header()
添加若 key
值已存在则会覆盖值内容。
public void demo1() {
Request request = new Request.Builder()
.url("http://127.0.0.1:8000")
// 重复覆盖
.header("Content-Type", "type-1.1")
.header("Content-Type", "type-1.2")
.build();
// [{Content-Type:type-1.2}]
System.out.println("request: " + request.headers());
}
2. addHeader
通过 addHeader()
添加若 key
值已存在则会添加多个 key
相同的键值对。
public void demo2() {
Request request = new Request.Builder()
.url("http://127.0.0.1:8000")
// 重复添加多个
.addHeader("Content-Type", "type-2.1")
.addHeader("Content-Type", "type-2.2")
.build();
// [{Content-Type:type-2.1}, {Content-Type:type-2.2}]
System.out.println("request: " + request.headers());
}
3. headers
通过 headers()
作用效果等价 addHeader()
,对于重复的 key
不会覆盖,在存入多个请求头时其操作更方便。
public void demo3() {
Headers.Builder headers = new Headers.Builder();
headers.add("Content-Type", "type-3.1");
headers.add("Content-Type", "type-3.2");
Request request = new Request.Builder()
.url("http://127.0.0.1:8000")
.headers(headers.build())
.build();
// [{Content-Type:type-3.1}, {Content-Type:type-3.2}]
System.out.println("request: " + request.headers());
}
三、数据类型
1. 请求体
HTTP
中不同请求类型对应的请求体类型值参考下表:
类型 | 属性值 |
---|---|
纯文本 | text/plain; |
Json对象 | application/json; |
表单数据 | application/x-www-form-urlencoded; |
二进制流数据(文件下载) | application/octet-stream; |
表单中文件上传 | multipart/form-data; |
2. Json对象
在 Spring Boot
接口开发中当传输 Java bean
对象时常用到 @RequestBody
注解,此类接口使用 okhttp
发送请求时需设置媒体类型为 Json
。
其中 type
用于指定网络传输格式,内容参考上一点。
public RequestBody createBody() {
User user = new User("Alex");
String data = new ObjectMapper().writeValueAsString(user);
// 构建请求体
String type = "application/json;";
MediaType mediaType = MediaType.parse(type);
return RequestBody.create(data, mediaType);
}
3. 表单对象
与 Json
对象相对应的即表单对账,在 Spring Boot
接口中通常使用 @RequestParam
注解标识。
传输表单数据一共有两种方式,一种是类似上面设置对应的媒体类型,另一种则通过 FormBody
实现。
/**
* 方式一:指定媒体类型为表单
*/
public RequestBody createBody1() {
User user = new User("Alex");
String data = new ObjectMapper().writeValueAsString(user);
// 构建请求体
String type = "application/x-www-form-urlencoded;";
MediaType mediaType = MediaType.parse(type);
return RequestBody.create(data, mediaType);
}
/**
* 方式二:通过 FormBody 构建
*/
public RequestBody createBody2() {
// 测试数据
Map<String, String> paramsMap = new HashMap<>();
paramsMap.put("name", "Alex");
// 构建请求体
FormBody.Builder builder = new FormBody.Builder();
paramsMap.keySet()
.forEach(it -> {
builder.add(it, paramsMap.get(it));
});
return builder.build();
}
四、网络请求
1. GET请求
使用 Get
方式请求将参数直接拼接在 url
中即可。
public Request createGetReq() {
// 构建请求
return new Request.Builder()
.url("http://127.0.0.1:8000?id=1&name=Alex")
.get()
.build();
}
2. POST请求
使用 Post
请求方式则根据数据类型按照上述中先构建请求头再传入 post()
中。
public Request createPostReq() {
// 构建对象
User user = new User("Alex");
String data = new ObjectMapper().writeValueAsString(user);
// 设置数据格式
String type = "application/x-www-form-urlencoded;";
MediaType mediaType = MediaType.parse(type);
RequestBody body = RequestBody.create(data, mediaType);
// 构建请求
return new Request.Builder()
.url("http://127.0.0.1:8000")
// 传入请求体
.post(body)
.build();
}
3. 同步请求
通过 newCall().execute()
发送同步请求,此时线程进入阻塞,当接收到请求响应后才会继续后续内容。
public String callRequest(OkHttpClient client, Request request) {
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
// 响应失败
throw new RuntimeException("Unexpected code: " + response);
}
// 获取响应体内容
ResponseBody body = response.body();
if (body == null) {
throw new RuntimeException("Response body is null");
}
return body.string();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
4. 异步请求
通过 enqueue()
方法的异步回调实现异步网络请求,可配合 volatile
等关键字设置状态实现异步转同步监控。
public void callAsyncRequest(OkHttpClient client, Request request) {
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
throw new RuntimeException(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
throw new RuntimeException("Unexpected code: " + response);
}
// 获取请求结果
ResponseBody body = response.body();
if (body == null) {
throw new RuntimeException("Response body is null");
}
String result = body.string();
System.out.println("Result: " + result);
}
});
}