HTTP GET请求示例代码:

```java

import okhttp3.OkHttpClient;

import okhttp3.Request;

import okhttp3.Response;

public class HttpGetExample {

public static void main(String[] args) throws IOException {

OkHttpClient client = new OkHttpClient();

String url = "https://api.example.com/data";

String result = run(url);

System.out.println(result);

}

public static String run(String url) throws IOException {

Request request = new Request.Builder().url(url).build();

Response response = client.newCall(request).execute();

if (response.isSuccessful()) {

return response.body().string();

} else {

throw new IOException("Unexpected code " + response);

}

}

}

```

Response类示例代码:

```java

import okhttp3.Response;

public class ResponseExample {

public static void main(String[] args) {

// 这里需要一个实际的响应对象,例如从网络请求中获取的Response对象

Response response = null;

boolean isSuccessful = response.isSuccessful();

System.out.println("Is successful: " + isSuccessful);

}

}

```

```java

public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

String post(String url, Map params) throws IOException {

StringBuilder json = new StringBuilder();

for (Map.Entry entry : params.entrySet()) {

if (json.length() != 0) {

json.append('&');

}

json.append(URLEncoder.encode(entry.getKey(), "UTF-8"));

json.append('=');

json.append(URLEncoder.encode(entry.getValue(), "UTF-8"));

}

RequestBody body = RequestBody.create(JSON, json.toString());

Request request = new Request.Builder()

.url(url)

.post(body)

.build();

Response response = client.newCall(request).execute();

if (response.isSuccessful()) {

return response.body().string();

} else {

throw new IOException("Unexpected code " + response);

}

}

```

Response response = client.newCall(request).execute();

if (response.isSuccessful()) {

return response.body().string();

} else {

throw new IOException("Unexpected code " + response);

}

}

总结:通过上面的例子我们可以发现,OkHttp在很多时候使用都是很方便的,而且很多代码也有重复。因此,特地整理了下面的工具类。注意:OkHttp官方文档并不建议我们创建多个OkHttpClient,因此全局使用一个。如果有需要,可以使用clone方法,再进行自定义。这点在后面的高级教程里会提到。enqueue为OkHttp提供的异步方法,入门教程中并没有提到,后面的高级教程里会有解释。

```java

import java.io.IOException;

import java.util.List;

import java.util.concurrent.TimeUnit;

import org.apache.http.client.utils.URLEncodedUtils;

import org.apache.http.message.BasicNameValuePair;

import cn.wiz.sdk.constant.WizConstant;

import com.squareup.okhttp.Callback;

import com.squareup.okhttp.OkHttpClient;

import com.squareup.okhttp.Request;

import com.squareup.okhttp.Response;

public class OkHttpUtil {

private static final OkHttpClient mOkHttpClient = new OkHttpClient();

static{

mOkHttpClient.setConnectTimeout(30, TimeUnit.SECONDS);

}

/**

* 该不会开启异步线程。

* @param request

* @return

*/

public String sendRequest(Request request) throws IOException {

Response response = client.newCall(request).execute();

if (response.isSuccessful()) {

return response.body().string();

} else {

throw new IOException("Unexpected code " + response);

}

}

```

这是一个使用OkHttp库发送网络请求的Java代码。重构后的内容如下:

```java

/**

* @throws IOException

*/

public static Response execute(Request request) throws IOException {

return mOkHttpClient.newCall(request).execute();

}

/**

* 开启异步线程访问网络

* @param request

* @param responseCallback

*/

public static void enqueue(Request request, final Call.Callback responseCallback) {

mOkHttpClient.newCall(request).enqueue(responseCallback);

}

/**

* 开启异步线程访问网络, 且不在意返回结果(实现空callback)

* @param request

*/

public static void enqueue(Request request) {

mOkHttpClient.newCall(request).enqueue(new Call.Callback() {

@Override

public void onResponse(Call call, Response response) throws IOException {

}

@Override

public void onFailure(Call call, IOException e) {

}

});

}

/**

* 从服务器获取字符串数据

* @param url String类型,表示服务器的URL地址

* @return 返回服务器响应的数据字符串,如果请求不成功,抛出IOException异常

* @throws IOException

*/

public static String getStringFromServer(String url) throws IOException {

Request request = new Request.Builder().url(url).build();

Response response = execute(request);

if (response.isSuccessful()) {

String responseUrl = response.body().string();

return responseUrl;

} else {

throw new IOException("Unexpected code " + response);

}

}

```

重构后的内容如下:

```java

private static final String CHARSET_NAME = "UTF-8";

/**

* 这里使用了HttpClinet的API。只是为了方便

* @param params

* @return

*/

public static String formatParams(List params){

return URLEncodedUtils.format(params, CHARSET_NAME);

}

/**

* 为HttpGet 的 url 方便的添加多个name value 参数。

* @param url

* @param params

* @return

*/

public static String attachHttpGetParams(String url, List params){

return url + "?" + formatParams(params);

}

/**

* 为HttpGet 的 url 方便的添加1个name value 参数。

* @param url

* @param name

* @param value

* @return

*/

public static String attachHttpGetParam(String url, String name, String value){

return url + "?" + name + "=" + value;

}

}

```

重构后的代码:

```javapublic void run() throws Exception {

Request request = new Request.Builder()

.url("http://publicobject.com/helloworld.txt")

.build();

client.newCall(request).enqueue(new Callback() {

@Override

public void onFailure(Request request, Throwable throwable) {

throwable.printStackTrace();

}

@Override

public void onResponse(Response response) throws IOException {

if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

Headers responseHeaders = response.headers();

for (int i = 0; i < responseHeaders.size(); i++) {

System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));

}

System.out.println(response.body().string());

}

});

}

```

```java

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {

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();

Response response = client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

Headers responseHeaders = response.headers();

for (int i = 0; i < responseHeaders.size(); i++) {

System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));

}

System.out.println(response.body().string());

}

```

以下是重构后的代码:

```java

import java.io.IOException;

import okhttp3.MediaType;

import okhttp3.OkHttpClient;

import okhttp3.Request;

import okhttp3.RequestBody;

import okhttp3.Response;

public class Main {

public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {

String postBody = "Releases" +

"--------" +

"" +

" * _1.0_ May 6, 2013" +

" * _1.1_ June 15, 2013" +

" * _1.2_ August 11, 2013";

Request request = new Request.Builder()

.url("https://api.github.com/markdown/raw")

.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))

.build();

Response response = client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

}

}

```

这段代码主要完成了以下功能:

1. 通过System.out.println()方法输出response的body内容。

2. 定义了一个MediaType对象MEDIA_TYPE_MARKDOWN,用于指定请求的内容类型为Markdown文本。

3. 创建了一个OkHttpClient对象client,用于发送网络请求。

4. 在run()方法中,构建了一个RequestBody对象requestBody,该对象包含了要发送的Markdown文本内容。同时,还定义了一个名为factor()的方法,用于计算给定数值的阶乘。

5. 使用Request.Builder构建了一个POST请求,将requestBody作为请求体发送到指定的URL(https://api.github.com/markdown/raw)。

Response response = client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

System.out.println(response.body().string());

}

Post方式提交文件

以文件作为请求体是十分简单的。

```

public static final MediaType MEDIA_TYPE_MARKDOWN

= MediaType.parse("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {

File file = new File("README.md");

Request request = new Request.Builder()

.url("https://api.github.com/markdown/raw")

.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))

.build();

Response response = client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

System.out.println(response.body().string());

}

```

Post方式提交表单

使用FormEncodingBuilder来构建表单数据:

```

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {

FormEncodingBuilder formBody = new FormEncodingBuilder();

formBody.add("key", "value"); // key-value pairs for the form fields

// ... add more fields as needed

Request request = new Request.Builder()

.url("https://example.com/submit")

.post(formBody.build())

.build();

Response response = client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

System.out.println(response.body().string());

}

```

以下是重构后的内容:

```java

import okhttp3.*;

import java.io.IOException;

import java.util.HashMap;

import java.util.Map;

public class Main {

private static final String IMGUR_CLIENT_ID = "your_imgur_client_id";

private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");

private final OkHttpClient client = new OkHttpClient();

public void uploadImg(String imagePath) throws Exception {

RequestBody requestBody = new MultipartBuilder()

.type(MultipartBuilder.FORM)

.addPart(

Headers.of("Content-Disposition", "form-data; name=\"file\";filename=\"" + imagePath),

RequestBody.create(new File(imagePath), MEDIA_TYPE_PNG))

.build();

Request request = new Request.Builder()

.url("https://api.imgur.com/3/image")

.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)

.post(requestBody)

.build();

Response response = client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

System.out.println(response.body().string());

}

public static void main(String[] args) throws Exception {

Main main = new Main();

main.uploadImg("path/to/your/image.png");

}

}

```

首先,需要导入Gson和相关依赖。在项目的build.gradle文件中添加以下依赖:

```groovy

implementation 'com.google.code.gson:gson:2.8.9'

implementation 'com.squareup.okhttp3:okhttp:4.9.1'

```

然后,创建一个名为ImgurClient的类,并在其中添加以下代码:

```java

import java.io.File;

import okhttp3.MediaType;

import okhttp3.OkHttpClient;

import okhttp3.Request;

import okhttp3.RequestBody;

import okhttp3.Response;

import com.google.gson.Gson;

public class ImgurClient {

private static final String IMGUR_CLIENT_ID = "your_client_id";

private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");

private OkHttpClient client = new OkHttpClient();

private Gson gson = new Gson();

public void uploadImage() throws Exception {

File file = new File("website/static/logo-square.png");

RequestBody requestBody = RequestBody.create(MEDIA_TYPE_PNG, file);

Headers headers = new Headers.Builder()

.add("Content-Disposition", "form-data; name=\"image\"")

.build();

Request request = new Request.Builder()

.headers(headers)

.url("https://api.imgur.com/3/image")

.post(requestBody)

.build();

Response response = client.newCall(request).execute();

if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

System.out.println(response.body().string());

}

}

```

在这个类中,我们创建了一个名为uploadImage的方法,该方法使用OkHttpClient发送一个POST请求到Imgur API。我们还使用了Gson库来解析JSON响应。请确保将"your_client_id"替换为您的实际Imgur客户端ID。

以下是根据给定内容重构后的代码:

```java

if (!response.isSuccessful()) {

throw new IOException("Unexpected code " + response);

}

Gist gist = gson.fromJson(response.body().charStream(), Gist.class);

for (Map.Entry entry : gist.files.entrySet()) {

System.out.println(entry.getKey());

System.out.println(entry.getValue().content);

}

}

static class Gist {

Map files;

}

static class GistFile {

String content;

}

public static void main(String[] args) throws Exception {

CacheResponse cacheResponse = new CacheResponse(new File("cache")); // 设置缓存目录

cacheResponse.run(); // 发起请求并获取响应

}

static class OkHttpClient extends okhttp3.OkHttpClient {

Cache cache;

public OkHttpClient() {

super();

}

public OkHttpClient setCache(Cache cache) {

this.cache = cache;

return this;

}

@Override

public Call newCall(Request request) {

Call call = super.newCall(request);

call.enqueue(new okhttp3.Callback() {

@Override

public void onFailure(Call call, okhttp3.Response response) throws IOException {

if (cache != null && response.code() == okhttp3.StatusCode.SC_NOT_MODIFIED) { // 如果响应状态码为304且缓存存在,则从缓存中获取数据并返回

Response cachedResponse = cache.get(response);

if (cachedResponse != null) {

runSuccess(cachedResponse);

} else { // 如果缓存中不存在该资源,则直接返回失败响应

runFailure(response);

}

} else if (cache != null) { // 如果响应状态码不是304且缓存存在,则将响应结果存入缓存并返回成功响应

Response response1 = call.request().newBuilder().url(response.request().url()).build(); // 从原始请求中构建新的请求以便使用相同的缓存策略

Response response2 = client.newCall(response1).execute(); // 从服务器获取新响应,如果没有修改则返回缓存中的数据,否则更新缓存并返回新响应数据

if (response2 != null && response2.code() == okhttp3.StatusCode.SC_NOT_MODIFIED) { // 如果新响应状态码为304,则从缓存中获取数据并返回成功响应,否则更新缓存并返回新响应数据

Response cachedResponse = cache.get(response1);

if (cachedResponse != null) {

runSuccess(cachedResponse);

} else { // 如果缓存中不存在该资源,则直接返回失败响应,并清空缓存以便下次请求使用新的数据

runFailure(response2);

cache.clear();

}

} else if (response2 != null) { // 如果新响应状态码不是304且有新数据,则清空缓存并将新数据存入缓存并返回成功响应,同时清空缓存以便下次请求使用新的数据

tryToClearCache(response1); // 将原始请求的缓存清空以便下次请求使用新的数据,但不会影响其他请求的缓存数据。这里假设已经实现了tryToClearCache方法。如果没有实现,可以根据实际情况进行调整。

Response cachedResponse = cache.get(response1);

if (cachedResponse != null) {

runSuccess(cachedResponse); // 将新请求的结果存入缓存并返回成功响应,同时清空缓存以便下次请求使用新的数据。这里假设已经实现了runSuccess和runFailure方法。如果没有实现,可以根据实际情况进行调整。

```java

if (!response1.isSuccessful()) throw new IOException(“Unexpected code ” + response1);

String response1Body = response1.body().string();

System.out.println(“Response 1 response: ” + response1);

System.out.println(“Response 1 cache response: ” + response1.cacheResponse());

System.out.println(“Response 1 network response: ” + response1.networkResponse());

Response response2 = client.newCall(request).execute();

if (!response2.isSuccessful()) throw new IOException(“Unexpected code ” + response2);

String response2Body = response2.body().string();

System.out.println(“Response 2 response: ” + response2);

System.out.println(“Response 2 cache response: ” + response2.cacheResponse());

System.out.println(“Response 2 network response: ” + response2.networkResponse());

System.out.println(“Response 2 equals Response 1? ” + response1Body.equals(response2Body));

}

```

在某些情况下,例如用户点击“刷新”按钮后,可能需要跳过缓存并直接从服务器获取数据。要强制完全刷新,请添加no-cache指令:

connection.addRequestProperty("Cache-Control", "no-cache");

如果仅需要强制验证缓存响应是否有效,请使用更高效的max-age=0:

connection.addRequestProperty("Cache-Control", "max-age=0");

强制验证网络响应

有时,我们需要确保网络响应经过验证,可以使用条件GET来实现。条件GET是一个带有查询参数的GET请求,只有当资源内容发生变化时,服务器才会返回新的响应。要实现条件GET,可以在URL中添加一个版本参数,如下所示:

String url = "http://example.com/data?version=1"; // 每次访问时版本号递增 1

HttpURLConnection connection = (HttpURLConnection) url.openConnection();

connection.setRequestMethod("GET");

int responseCode = connection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) { // 成功获取数据

BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

String line;

StringBuilder response = new StringBuilder();

while ((line = reader.readLine()) != null) {

response.append(line);

}

reader.close();

} else {

// 处理错误情况

}

在某些情况下,您可能希望仅在资源可用时显示它们,而在其他情况下不显示。这可以用于在等待最新数据下载时向应用程序展示一些内容。要限制请求仅针对本地缓存的资源,请添加`only-if-cached`指令:

```java

try {

connection.addRequestProperty("Cache-Control", "only-if-cached");

InputStream cached = connection.getInputStream();

// 资源已经被缓存!显示它

} catch (FileNotFoundException e) {

// 资源未被缓存

}

```

这种技术在以下情况下表现得更好:滞后的响应比没有响应更好。要允许滞后的缓存响应,请使用`max-stale`指令并设置最大滞后时间(以秒为单位):

```java

int maxStale = 60 * 60 * 24 * 28; // 可容忍4周的滞后

connection.addRequestProperty("Cache-Control", "max-stale=" + maxStale);

```

取消一个Call

取消请求的方法有三种:`Call.cancel()`、`RequestBuilder.tag(tag)`和`OkHttpClient.cancel(tag)`。使用其中的一种方法即可取消请求。

```java

import java.io.IOException;

import java.util.concurrent.*;

import okhttp3.*;

public class MainClass {

private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {

Request request = new Request.Builder()

.url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.

.build();

final long startNanos = System.nanoTime();

final Call call = client.newCall(request);

// Schedule a job to cancel the call in 1 second.

executor.schedule(new Runnable() {

@Override

public void run() {

System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / 1e9f);

call.cancel();

System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / 1e9f);

}

}, 1, TimeUnit.SECONDS);

try {

System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / 1e9f);

Response response = call.execute();

System.out.printf("%.2f Call was expected to fail, but completed: %s%n", (System.nanoTime() - startNanos) / 1e9f, response);

} catch (IllegalStateException e) {

// If the call is already canceled, this exception will be thrown.

} finally {

// Release the resources held by the call object after it's finished executing or canceled.

call.close();

executor.shutdown();

}

}

}

```

以下是重构后的代码:

```java

import java.io.IOException;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;

import okhttp3.Request;

import okhttp3.Response;

public class ConfigureTimeouts {

private final OkHttpClient client;

public ConfigureTimeouts() throws Exception {

client = new OkHttpClient();

client.setConnectTimeout(10, TimeUnit.SECONDS);

client.setWriteTimeout(10, TimeUnit.SECONDS);

client.setReadTimeout(30, TimeUnit.SECONDS);

}

public void run() throws Exception {

Request request = new Request.Builder()

.url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.

.build();

Response response = client.newCall(request).execute();

System.out.println("Response completed: " + response);

}

}

```

这段代码首先构建了一个请求,其中包含了一个1秒延迟的URL。然后,它尝试使用两个不同的超时时间(500毫秒和3000毫秒)执行此请求。如果请求成功,将输出响应信息;如果失败,将输出异常信息。

处理验证部分,当响应码为401未授权时,返回相应的“WWW-Authenticate”挑战。

这段代码是一个Java程序,用于处理HTTP请求的代理认证。当响应码为407(代理未经授权)时,它会返回"Proxy-Authenticate"挑战。否则,它会返回一个空的挑战列表。

首先,我们创建一个`OkHttpClient`实例:

```java

private final OkHttpClient client = new OkHttpClient();

```

然后,在`run()`方法中,我们设置了一个自定义的认证器,用于处理代理认证:

```java

public void run() throws Exception {

client.setAuthenticator(new Authenticator() {

@Override public Request authenticate(Proxy proxy, Response response) {

System.out.println("Authenticating for response: " + response);

System.out.println("Challenges: " + response.challenges());

String credential = Credentials.basic("jesse", "password1");

return response.request().newBuilder()

.header("Authorization", credential)

.build();

}

@Override public Request authenticateProxy(Proxy proxy, Response response) {

return null; // Null indicates no attempt to authenticate.

}

});

}

```

接下来,我们构建一个请求对象,指定请求的URL:

```java

Request request = new Request.Builder()

.url("http://publicobject.com/secrets/hellosecret.txt")

.build();

```

最后,我们使用`client.newCall(request).execute()`方法发送请求并获取响应。

以下是重构后的代码:

```java

public void exampleMethod() throws IOException {

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()

.url("http://example.com")

.build();

try (Response response = client.newCall(request).execute()) {

if (!response.isSuccessful()) {

throw new IOException("Unexpected code " + response);

}

System.out.println(response.body().string());

}

}

```

在这个例子中,我将整个方法放入了一个类中,并添加了 `throws IOException` 注解以处理可能出现的异常。同时使用 try-with-resources 语句确保资源在执行完毕后被正确关闭。