OkHttp搞定Http请求
创始人
2024-03-16 19:54:28
0

文章目录

    • OkHttp
    • 依赖包
    • 创建OkHttpClient
    • 构造Url
    • Header头设定
    • Get请求
      • 同步Get
      • 异步Get
    • Post请求
      • Json内容
      • Form表单
    • 文件上传
    • Put请求
      • Json内容
      • 参数方式
    • Delete请求
    • OkHttp工具类

OkHttp

官方文档 仓库

OkHttp是一款高效的HTTP客户端框架:

  • HTTP/2支持允许对同一主机的所有请求共享一个套接字。
  • 连接池可以减少请求延迟(如果HTTP/2不可用)。
  • 透明GZIP压缩下载大小。
  • 响应缓存完全避免了重复请求的网络。

当网络出现问题时,OkHttp会自动恢复一般的连接问题;若服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP。

OkHttp采用流式构造接口,方便调用;同时支持同步与异步方式调用。

依赖包

OkHttp3是OkHttp发展到版本3.0之后的名字。

要使用OkHttp,需要先在pom.xml中引入依赖包;okhttp3是使用kotlin实现的,所以对应包也需要引入:

com.squareup.okhttp3okhttp4.9.1
org.jetbrains.kotlinkotlin-stdlib1.5.0

创建OkHttpClient

通过共享的响应缓存/线程池/复用的连接等,绝大多数应用只需一个OkHttpClient实例,便可以满足整个应用的所有Http请求。

OkHttpClient client = new OkHttpClient();OkHttpClient httpClient = new OkHttpClient.Builder().connectTimeout(60, TimeUnit.SECONDS) // default 10s.writeTimeout(30, TimeUnit.SECONDS)   // default 10s.readTimeout(30, TimeUnit.SECONDS)    // default 10s.build();OkHttpClient anotherClient  = client.newBuilder().build();

构造Url

通过HttpUrl.Builder可方便地构造Url:

  • addPathSegment:添加路径;
  • addQueryParameter:添加请求参数,允许重复;
  • setQueryParameter:设定请求参数,覆盖重复;
  • username、password:添加用户名、密码;
public static HttpUrl buildUrl(String url, Map queryParam) {HttpUrl.Builder builder = HttpUrl.parse(url).newBuilder();queryParam.forEach((k, v) -> {builder.addQueryParameter(k, v);});return builder.build();
}

构造完整Url的流程:

HttpUrl.Builder builder = new HttpUrl.Builder().host("127.0.0.1").port(8001).addPathSegment("seg1").addPathSegment("path2").username("user").password("pass").scheme("https").addQueryParameter("k1", "v1").addQueryParameter("k1", "v2").setQueryParameter("uk", "v1").setQueryParameter("uk", "v2");HttpUrl http = builder.build();
System.out.println(http.toString());
// https://user:pass@127.0.0.1:8001/seg1/path2?k1=v1&k1=v2&uk=v2

Header头设定

HTTP头(可参见请求头大全)是 Map>类型。也就是说,对于每个 HTTP 头,可能有多个值;但是大部分 HTTP 头都只有一个值。

OkHttp中通过Request构造时添加:

  • header(name,value):设置HTTP头的唯一值,若请求已经存在则替换掉。
  • addHeader(name,value):添加新值,若请求头中已经存在此name还会继续添加(此时,请求头中便会存在多个name相同而value不同的“键值对”)。
  • header(name):读取唯一值或多个值的最后一个值
  • headers(name):获取所有值

构造Request时,必须设定Url,默认是GET模式:

Request request = new Request.Builder().url("http://127.0.0.1").addHeader("h1", "v1").addHeader("h1", "v2").header("uh", "v1").header("uh", "v2").build();
System.out.println(request.toString());
// Request{method=GET, url=http://127.0.0.1/, headers=[h1:v1, h1:v2, uh:v2]}

请求示例

使用OkHttp,需要:

  • 构造OkHttpClient对象;
  • 构造Request对象;
  • 构造Call对象;
  • 发起请求:execute同步;enqueue异步;
  • 返回Response:不可修改的Http相应对象。

Get请求

Request默认就是Get请求,所以构造时可以省略GetGet请求参数通过queryParameter添加。

同步Get

通过newCall会同步调用,其返回内容可通过body来获取;

private static void getSync() throws IOException {String url = "http://wwww.baidu.com";Request request = new Request.Builder().url(url) // 可以字符串,也可以是HttpUrl.build();Call call = httpClient.newCall(request);Response resp = call.execute();if (resp.code() == 200) {System.out.println("Response: " + resp.body().string());} else {// Error handleSystem.out.println("Code:" + resp.code() + ", Msg:" + resp.message());}
}

异步Get

通过enqueue,可提交异步请求;请求的应答通过回调Callback返回。

private static void getAsync() {HttpUrl url = buildUrl("http://127.0.0.1:7087/study/test/testEvent",new HashMap(){{put("msg", "Test Msg event");}});// http://127.0.0.1:7087/study/test/testEvent?msg=Test%20Msg%20eventRequest request = new Request.Builder().url(url).get()  //默认就是GET请求,可以不写.build();Call call = httpClient.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {System.out.println("Fail: " + e.getMessage());}@Overridepublic void onResponse(Call call, Response resp) throws IOException {System.out.println("onResponse: " + resp.body().string());}});
}

Post请求

Request中通过Post来标识Post请求,并设定PostBody内容。

Json内容

Json是常用的序列化方式,只需把要传递的对象序列化为Json字符串,然后以字符串Body的方式传递到服务端。

private static void postJsonBody() throws IOException {MediaType mediaType = MediaType.parse("application/json;charset=UTF-8");RequestBody reqBody = RequestBody.create("msg for test", mediaType);Request request = new Request.Builder().url("http://127.0.0.1:7087/study/test/postMsg").post(reqBody).build();Response resp = httpClient.newCall(request).execute();System.out.println("Response: " + resp.body().string());
}

Form表单

Form表单都是以键值对的方式传递内容到服务端的,通过FormBody可方便地构造。

private static void postFormBody() throws IOException {RequestBody reqBody = new FormBody.Builder().add("msg", "form test").add("others", "other params").build();Request request = new Request.Builder().url("http://127.0.0.1:7087/study/test/formMsg").post(reqBody).build();Response resp = httpClient.newCall(request).execute();System.out.println("Response: " + resp.body().string());
}

文件上传

上传文件时,使用MultipartBody,并可通过MediaType来设定媒体类型:

private static void uploadOneFile() throws IOException {File file = new File("D:\\tmp\\Python-learning.md");MediaType mediaType = MediaType.parse("application/octet-stream"); //设置类型为八位字节流RequestBody reqBody = RequestBody.create(file, mediaType);MultipartBody multiBody = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("id", "0") // for test.addFormDataPart("file", file.getName(), reqBody).build();Request request = new Request.Builder()
//            .header("Authorization", "Bearer ****************") //添加请求头的身份认证Token.url("http://127.0.0.1:7087/study/test/uploadFile").post(multiBody).build();Response resp = httpClient.newCall(request).execute();System.out.println("Response: " + resp.body().string());
}

要上传多个文件,就需要构造多份文件相关的RequestBody,然后依次添加到MultipartBody中:

private static void uploadMultiFile() throws IOException {List lstFile = Lists.newArrayList("D:\\tmp\\Python-learning.md", "D:\\tmp\\WebRTC-Learning.md");MediaType mediaType = MediaType.parse("application/octet-stream"); //设置类型为八位字节流MultipartBody.Builder multiBody = new MultipartBody.Builder().setType(MultipartBody.FORM);for(String f : lstFile){File file = new File(f);RequestBody fileBody = RequestBody.create(file, mediaType);multiBody.addFormDataPart("file", file.getName(), fileBody);}Request request = new Request.Builder().url("http://127.0.0.1:7087/study/test/uploadMultiFile").post(multiBody.build()).build();Response resp = httpClient.newCall(request).execute();System.out.println("Response: " + resp.body().string());
}

Put请求

PutPost类似,只是Request时用Put标识。

Json内容

以传递Json格式的Body为例:

MediaType mediaType = MediaType.parse("application/json;charset=UTF-8");
RequestBody reqBody = RequestBody.create("{\"page\":0,\"query\":\"info to query\",\"size\":0}", mediaType);Request request = new Request.Builder().url("http://127.0.0.1:7087/study/test/testPut").put(reqBody).build();
Response resp = httpClient.newCall(request).execute();
System.out.println("Response: " + resp.body().string());

参数方式

通过参数来传递请求的内容;但是Put要求必须传递Body,此时可构造一个空Body

HttpUrl url = buildUrl("http://127.0.0.1:7087/study/test/paramPut",new HashMap() {{put("msg", "Test Msg event");put("others", "other params");}});
RequestBody reqBody = RequestBody.create(new byte[]{}, null);
Request request = new Request.Builder().url(url).put(reqBody).build();
Response resp = httpClient.newCall(request).execute();
System.out.println("Response: " + resp.body().string());

Delete请求

Request中通过Delete来标识请求,默认是通过参数方式的,也可通过RequestBody来传递。

HttpUrl url = buildUrl("http://127.0.0.1:7087/study/test/testDelete",new HashMap() {{put("id", "delId");}});
Request request = new Request.Builder().url(url).delete().build();
Response resp = httpClient.newCall(request).execute();
System.out.println("Response: " + resp.body().string());

OkHttp工具类

下面是一个封装的工具类,里边包含了日常用到的一些请求方式 和 日志打印 http异常捕获,可以根据实际需要进行裁剪使用

HttpUtil.java

package org.example.utils;import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.example.exception.HttpStatusException;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;import java.io.File;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.*;
import java.util.concurrent.TimeUnit;@Slf4j
public class HttpUtil {private static OkHttpClient client;private static final String DEFAULT_MEDIA_TYPE = "application/json; charset=utf-8";private static final int CONNECT_TIMEOUT = 5;private static final int READ_TIMEOUT = 7;private static final String GET = "GET";private static final String POST = "POST";/*** 单例模式  获取类实例** @author: yh* @date: 2022/12/1*/private static OkHttpClient getInstance() {if (client == null) {synchronized (OkHttpClient.class) {if (client == null) {client = new OkHttpClient.Builder()// 设置默认连接超时时间。值为0表示没有超时,否则值必须在1和Integer之间.connectTimeout(60, TimeUnit.SECONDS)// 设置新连接的默认写超时时间。值为0表示没有超时,否则值必须在1和Integer之间.writeTimeout(30, TimeUnit.SECONDS)// 设置新连接的默认读超时时间。值为0表示没有超时,否则值必须在1和Integer之间.readTimeout(30, TimeUnit.SECONDS).build();}}}return client;}/*** 构造 Url** @param url        请求路径* @param queryParam 参数* @return HttpUrl*/public static HttpUrl buildUrl(String url, Map queryParam) {HttpUrl.Builder builder = HttpUrl.parse(url).newBuilder();// addPathSegment:添加路径;// addQueryParameter:添加请求参数,允许重复;// setQueryParameter:设定请求参数,覆盖重复;// username、password:添加用户名、密码;queryParam.forEach(builder::addQueryParameter);return builder.build();}/*** 普通 GET 请求** @param url 请求路径* @return response* @author: yh* @date: 2022/12/1*/public static String doGet(String url) {try {long startTime = System.currentTimeMillis();
//            addRequestLog(GET, url, null, null);Request request = new Request.Builder().url(url).build();// 创建一个请求Response response = getInstance().newCall(request).execute();int httpCode = response.code();String result;ResponseBody body = response.body();if (body != null) {result = body.string();
//                addResponseLog(httpCode, result, startTime);} else {throw new RuntimeException("exception in OkHttpUtil,response body is null");}return handleHttpResponse(httpCode, result);} catch (Exception ex) {handleHttpThrowable(ex, url);return StringUtils.EMPTY;}}/*** 携带 HEADER 的 GET 请求** @param url     请求路径* @param headers 请求头* @return response* @author: yh* @date: 2022/12/1*/public static String doGet(String url, Headers headers) {try {long startTime = System.currentTimeMillis();
//            addRequestLog(GET, url, null, null);Request request = new Request.Builder().url(url).headers(headers).build();// 创建一个请求Response response = getInstance().newCall(request).execute();int httpCode = response.code();String result;ResponseBody body = response.body();if (body != null) {result = body.string();
//                addResponseLog(httpCode, result, startTime);} else {throw new RuntimeException("exception in OkHttpUtil,response body is null");}return handleHttpResponse(httpCode, result);} catch (Exception ex) {handleHttpThrowable(ex, url);return StringUtils.EMPTY;}}/*** POST 普通请求** @param url       请求路径* @param postBody  参数* @param mediaType mediaType 默认:json* @return response* @author: yh* @date: 2022/12/1*/public static String doPost(String url, String postBody, String mediaType){try {long startTime = System.currentTimeMillis();
//            addRequestLog(POST, url, postBody, null);MediaType createMediaType = MediaType.parse(mediaType == null ? DEFAULT_MEDIA_TYPE : mediaType);Request request = new Request.Builder().url(url).post(RequestBody.create(createMediaType, postBody)).build();Response response = getInstance().newCall(request).execute();int httpCode = response.code();String result;ResponseBody body = response.body();if (body != null) {result = body.string();
//                addResponseLog(httpCode, result, startTime);} else {throw new IOException("exception in OkHttpUtil,response body is null");}return handleHttpResponse(httpCode, result);} catch (Exception ex) {handleHttpThrowable(ex, url);return StringUtils.EMPTY;}}/*** POST 表单提交** @param url          请求路径* @param parameterMap 请求参数* @return response* @author: yh* @date: 2022/12/1*/public static String doPost(String url, Map parameterMap) {try {long startTime = System.currentTimeMillis();
//            List parameterList = new ArrayList<>();FormBody.Builder builder = new FormBody.Builder();if (parameterMap.size() > 0) {for (Map.Entry entry : parameterMap.entrySet()) {String parameterName = entry.getKey();String value = entry.getValue();builder.add(parameterName, value);
//                    parameterList.add(parameterName + ":" + value);}}
//            addRequestLog(POST, url, null, Arrays.toString(parameterList.toArray()));FormBody formBody = builder.build();Request request = new Request.Builder().url(url).post(formBody).build();Response response = getInstance().newCall(request).execute();String result;int httpCode = response.code();ResponseBody body = response.body();if (body != null) {result = body.string();addResponseLog(httpCode, result, startTime);} else {throw new IOException("exception in OkHttpUtil,response body is null");}return handleHttpResponse(httpCode, result);} catch (Exception ex) {handleHttpThrowable(ex, url);return StringUtils.EMPTY;}}/*** POST 上传单个文件** @param url  请求路径* @param file 文件* @return response* @author: yh* @date: 2022/12/1*/public static String uploadFile(String url, File file) {try {//设置类型为八位字节流MediaType mediaType = MediaType.parse("application/octet-stream");RequestBody reqBody = RequestBody.create(file, mediaType);MultipartBody multiBody = new MultipartBody.Builder().setType(MultipartBody.FORM)// 设置参数.addFormDataPart("id", "0").addFormDataPart("file", file.getName(), reqBody).build();Request request = new Request.Builder().url(url).post(multiBody).build();Response response = getInstance().newCall(request).execute();String result;int httpCode = response.code();ResponseBody body = response.body();if (body != null) {result = body.string();} else {throw new IOException("exception in OkHttpUtil,response body is null");}return handleHttpResponse(httpCode, result);} catch (Exception ex) {handleHttpThrowable(ex, url);return StringUtils.EMPTY;}}/*** POST 上传多个文件** @param url   请求路径* @param files 文件集合* @return response* @author: yh* @date: 2022/12/1*/public static String uploadMultiFile(String url, List files) {try {//设置类型为八位字节流MediaType mediaType = MediaType.parse("application/octet-stream");MultipartBody.Builder multiBody = new MultipartBody.Builder().setType(MultipartBody.FORM);for (File file : files) {RequestBody fileBody = RequestBody.create(file, mediaType);multiBody.addFormDataPart("file", file.getName(), fileBody);}Request request = new Request.Builder().url(url).post(multiBody.build()).build();Response response = getInstance().newCall(request).execute();String result;int httpCode = response.code();ResponseBody body = response.body();if (body != null) {result = body.string();} else {throw new IOException("exception in OkHttpUtil,response body is null");}return handleHttpResponse(httpCode, result);} catch (Exception ex) {handleHttpThrowable(ex, url);return StringUtils.EMPTY;}}/*** PUT 提交** @param url       请求路径* @param jsonParam 请求参数* @return response* @author: yh* @date: 2022/12/1*/public static String doPut(String url, String jsonParam) {try {MediaType mediaType = MediaType.parse("application/json;charset=UTF-8");RequestBody reqBody = RequestBody.create(jsonParam, mediaType);Request request = new Request.Builder().url(url).put(reqBody).build();Response response = getInstance().newCall(request).execute();String result;int httpCode = response.code();ResponseBody body = response.body();if (body != null) {result = body.string();} else {throw new IOException("exception in OkHttpUtil,response body is null");}return handleHttpResponse(httpCode, result);} catch (Exception ex) {handleHttpThrowable(ex, url);return StringUtils.EMPTY;}}/*** PUT 提交** @param url      请求路径* @param paramMap 请求参数 Map类型* @return response* @author: yh* @date: 2022/12/1*/public static String doPut(String url, Map paramMap) {try {HttpUrl httpUrl = buildUrl(url, paramMap);RequestBody reqBody = RequestBody.create(new byte[]{}, null);Request request = new Request.Builder().url(httpUrl).put(reqBody).build();Response response = getInstance().newCall(request).execute();String result;int httpCode = response.code();ResponseBody body = response.body();if (body != null) {result = body.string();} else {throw new IOException("exception in OkHttpUtil,response body is null");}return handleHttpResponse(httpCode, result);} catch (Exception ex) {handleHttpThrowable(ex, url);return StringUtils.EMPTY;}}/*** DELETE 请求** @param url      请求路径* @param paramMap 请求参数 Map类型* @return response* @author: yh* @date: 2022/12/1*/public static String doDelete(String url, Map paramMap) {try {HttpUrl httpUrl = buildUrl(url, paramMap);Request request = new Request.Builder().url(httpUrl).delete().build();Response response = getInstance().newCall(request).execute();String result;int httpCode = response.code();ResponseBody body = response.body();if (body != null) {result = body.string();} else {throw new IOException("exception in OkHttpUtil,response body is null");}return handleHttpResponse(httpCode, result);} catch (Exception ex) {handleHttpThrowable(ex, url);return StringUtils.EMPTY;}}/*** 请求前记录日志** @param method* @param url* @param body* @param formParam* @author: yh* @date: 2022/12/1*/private static void addRequestLog(String method, String url, String body, String formParam) {log.info("===========================request begin================================================");log.info("URI          : {}", url);log.info("Method       : {}", method);if (StringUtils.isNotBlank(body)) {log.info("Request body : {}", body);}if (StringUtils.isNotBlank(formParam)) {log.info("Request param: {}", formParam);}log.info("---------------------------request end--------------------------------------------------");}/*** 请求成功响应日志** @param httpCode  状态码* @param result    请求结果* @param startTime 请求前计时* @author: yh* @date: 2022/12/1*/private static void addResponseLog(int httpCode, String result, long startTime) {long endTime = System.currentTimeMillis();log.info("Status       : {}", httpCode);log.info("Response     : {}", result);log.info("Time         : {} ms", endTime - startTime);log.info("===========================response end================================================");}/*** 状态码不为 200 抛出异常** @param httpCode 响应状态码* @param result   响应结果* @return 响应结果* @author: yh* @date: 2022/12/1*/private static String handleHttpResponse(int httpCode, String result) throws HttpStatusException {if (httpCode == HttpStatus.OK.value()) {return result;}HttpStatus httpStatus = HttpStatus.valueOf(httpCode);throw new HttpStatusException(httpStatus);}/*** 打印 异常信息** @param ex  异常* @param url 请求的路径* @author: yh* @date: 2022/12/1*/private static void handleHttpThrowable(Exception ex, String url) {if (ex instanceof HttpStatusException) {
//            throw (HttpStatusException) ex;log.error("OkHttp exception: " + ex.getMessage());}log.error("OkHttp error url: " + url, ex);if (ex instanceof SocketTimeoutException) {throw new RuntimeException("request time out of OkHttp when do url:" + url);}throw new RuntimeException(ex);}}

Http请求异常类:HttpStatusException.java

package org.example.exception;import org.springframework.http.HttpStatus;/*** 异常类* @author: yh* @date: 2022/12/1*/
public class HttpStatusException extends Exception {private HttpStatus httpStatus;private String result;public HttpStatusException(HttpStatus httpStatus) {this.httpStatus = httpStatus;}public HttpStatusException(HttpStatus httpStatus, String result) {this.httpStatus = httpStatus;}public HttpStatus getHttpStatus() {return httpStatus;}public String getResult() {return result;}
}

调用示例:

// GET 请求携带请求头
Headers headers = new Headers.Builder().add("token", "123456").build();
String s = HttpUtil.doGet("https://127.0.0.1/test", headers);
System.out.println(s);
// ......

到此,本章内容就介绍完啦,如果有帮助到你 欢迎点个赞👍👍吧!!您的鼓励是博主的最大动力! 有问题评论区交流。

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...