# SpringBoot使用Okhttp3

记录下 Okhttp3

# 1. 简单使用

API 简单使用

# 1.1. Closed

// 下面的代码会触发关闭响应体,下次就无法再调用获取请求结果了
Response.close();
Response.body().close();
Response.body().source().close();
Response.body().charStream().close();
Response.body().byteString().close();
Response.body().bytes();
Response.body().string();
// 使用下面的方式解决,相当于复制,上面的代码还能继续使用
ResponseBody responseBody = response.peekBody(Long.MAX_VALUE);

# 1.2. Retry

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

import java.io.IOException;

/**
 * 请求重试拦截器
 *
 * @author wliduo[i@dolyw.com]
 * @date 2020/5/13 10:52
 */
@Slf4j
public class CustomRetryInterceptor implements Interceptor {

    /**
     * 默认最大重试次数
     */
    public int maxRetry = 3;

    public CustomRetryInterceptor() {}

    public CustomRetryInterceptor(int maxRetry) {
        this.maxRetry = maxRetry;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);
        for (int i = 1; i < maxRetry; i++) {
            // 响应成功且内容也是成功的才不进行重试,必须使用peekBody()获取ResponseBody,
            // 这样外部还能再获取ResponseBody
            ResponseBody responseBody = response.peekBody(Long.MAX_VALUE);
            String result = responseBody.string();
            if (response.isSuccessful()) {
                CommonResp commonResp = JSON.parseObject(result, CommonResp.class);
                int status = commonResp.getStatus();
                // 响应码为200到300之前才算成功
                if (status >= CommonResp.OK && status < CommonResp.MULTIPLE_CHOICES) {
                    break;
                }
            }
            // 请求异常开始重试
            log.debug("重试第{}次,请求地址:{},上次请求结果:{},", i, request.url(), result);
            // 发起重试请求
            response = chain.proceed(request);
        }
        return response;
    }
}

# 1.3. Get

/**
 * 回调签名验证
 *
 * @param sign
 * @return java.lang.Boolean
 * @throws
 * @author wliduo[i@dolyw.com]
 * @date 2020/5/13 18:42
 */
public Boolean saaVerify(String sign) {
    // 开始发起请求
    OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
    // 请求地址,请求头和参数
    String url = verifyUrl + "?sign=" + sign;
    Request request = new Request.Builder().url(url).get().build();
    // 返回结果
    String result = "";
    // 发起请求
    try (Response response = okHttpClient.newCall(request).execute()) {
        // 获取结果
        result = response.body().string();
        log.info("请求地址:{},请求结果:{}", url,result);
        return Boolean.parseBoolean(result);
    } catch (SocketTimeoutException e) {
        log.error("签名验证请求超时异常,请求地址:{}", url);
        return false;
    } catch (Exception e) {
        log.error("签名验证失败出现异常,请求结果:{},错误信息: {}", result, ExceptionUtil.getExceptionMessage(e));
        return false;
    }
}

# 1.4. Post

Post 几种类型

text/plain

/**
 * 获取Token
 *
 * @param
 * @return java.lang.String
 * @throws
 * @author wliduo[i@dolyw.com]
 * @date 2020/5/13 19:20
 */
public String getToken() {
    String token = redisTemplate.opsForValue().get("XXX:XXX:Token");
    if (StringUtils.isNotEmpty(token)) {
        return token;
    }
    // 开始发起请求
    OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
    // 请求地址,请求头和参数
    String url = caaBaseUrl + getToken;
    String content = "grant_type=client_credentials&client_id=" + clientId + "&client_secret=" + clientSecret;
    RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), content);
    Request request = new Request.Builder().url(url).post(requestBody).build();
    // 返回结果
    String result = "";
    // 发起请求
    try (Response response = okHttpClient.newCall(request).execute()) {
        // 获取结果
        result = response.body().string();
        log.info("请求地址:{},请求结果:{}", url, result);
        XXXToken xxxToken = JSON.parseObject(result, XXXToken.class);
        token = xxxToken.getToken_type() + " " + xxxToken.getAccess_token();
        redisTemplate.opsForValue().set("XXX:XXX:Token", token, 14000, TimeUnit.SECONDS);
        // 返回结果
        return token;
    } catch (SocketTimeoutException e) {
        log.error("获取Token请求超时异常,请求地址:{}", url);
        return null;
    } catch (Exception e) {
        log.error("获取Token失败出现异常,请求结果:{},错误信息: {}", result, ExceptionUtil.getExceptionMessage(e));
        return null;
    }
}

application/json

/**
 * 取消订单
 *
 * @param orderCode
 * @return com.xxx.R<?>
 * @throws Exception e
 * @author wliduo[i@dolyw.com]
 * @date 2020/5/13 17:21
 */
@Override
public R<String> cancelOrder(String orderCode) throws Exception {
    // 封装订单参数
    OrderReq orderReq = new OrderReq();
    // 订单编号
    orderReq.setOrderCode(orderCode);
    // 备注
    orderReq.setRemark("");
    // Company
    orderReq.setCompany(company);
    // 客户编号
    orderReq.setCno(cno);
    // 开始发起请求,添加重试拦截器
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(new CustomRetryInterceptor(maxRetry)).build();
    // 请求地址,请求头和参数
    String param = JSON.toJSONString(orderReq);
    RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), param);
    Request request = new Request.Builder().url(baseUrl + cancelOrder).post(requestBody).build();
    // 返回结果
    String result = "";
    // 发起请求
    try (Response response = okHttpClient.newCall(request).execute()) {
        // 获取结果
        result = response.body().string();
        log.info("请求地址:{},请求参数:{},请求结果:{}", baseUrl + cancelOrder, param, result);
        CommonResp commonResp = JSON.parseObject(result, CommonResp.class);
        // 服务商取消订单完成
        return new R<>(commonResp.getStatus(), commonResp.getMsg(), orderCode);
    } catch (SocketTimeoutException e) {
        log.error("取消订单请求超时异常,请求地址:{},请求参数:{}", baseUrl + cancelOrder, param);
        return new R<>(CommonResp.INTERNAL_SERVER_ERROR, "取消订单请求超时异常", orderCode);
    } catch (Exception e) {
        log.error("取消订单出现异常,请求结果:{},错误信息: {}", result, ExceptionUtil.getExceptionMessage(e));
        return new R<>(CommonResp.INTERNAL_SERVER_ERROR, result, orderCode);
    }
}

# 2. SpringBoot

SpringBoot 配置使用

# 2.1. Config

import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

/**
 * OkHttp3Config
 *
 * @author wliduo[i@dolyw.com]
 * @date 2022/7/14 10:10
 */
@Configuration
public class OkHttp3Config {

    /**
     * X509TrustManager
     *
     * @param
     * @return javax.net.ssl.X509TrustManager
     * @throws
     * @author wliduo[i@dolyw.com]
     * @date 2022/7/14 10:15
     */
    @Bean
    public X509TrustManager x509TrustManager() {
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };

    }

    /**
     * SSLSocketFactory
     *
     * @param
     * @return javax.net.ssl.SSLSocketFactory
     * @throws
     * @author wliduo[i@dolyw.com]
     * @date 2022/7/14 10:15
     */
    @Bean
    public SSLSocketFactory sslSocketFactory() {
        try {
            // 信任任何链接
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 连接数量
     *
     * @param
     * @return okhttp3.ConnectionPool
     * @throws
     * @author wliduo[i@dolyw.com]
     * @date 2022/7/14 10:15
     */
    @Bean
    public ConnectionPool pool() {
        return new ConnectionPool(200, 5, TimeUnit.MINUTES);
    }

    /**
     * 超时时间
     *
     * @param
     * @return okhttp3.OkHttpClient
     * @throws
     * @author wliduo[i@dolyw.com]
     * @date 2022/7/14 10:15
     */
    @Bean
    public OkHttpClient okHttpClient() {
        return new OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory(), x509TrustManager())
                // 禁用缓存,防止调用失败
                // https://blog.csdn.net/u010248147/article/details/90479276
                .retryOnConnectionFailure(true)
                // 连接池
                .connectionPool(pool())
                .connectTimeout(60L, TimeUnit.SECONDS)
                .readTimeout(60L, TimeUnit.SECONDS)
                .build();
    }

}

# 2.2. Helper

package com.pcic.helper;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Method;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;

/**
 * OkHttp3Helper
 *
 * @author wliduo[i@dolyw.com]
 * @date 2021/8/23 10:08
 */
@Slf4j
@Component
public class OkHttp3Helper {

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

    /**
     * MEDIA_TYPE_TEXT
     */
    public static final MediaType MEDIA_TYPE_TEXT = MediaType.parse("text/plain;charset=utf-8");

    @Autowired
    private OkHttpClient okHttpClient;

    /**
     * Get请求
     *
     * @param url
     * @return java.lang.String
     * @throws
     * @author wliduo[i@dolyw.com]
     * @date 2021/8/23 10:18
     */
    public String get(String url) {
        String result = StrUtil.EMPTY;
        Request request = new Request.Builder().url(url).build();
        try (Response response = okHttpClient.newCall(request).execute()) {
            result = response.body().string();
            log.info("请求地址:{},请求结果:{}", url, result);
        } catch (Exception e) {
            log.error("请求地址:{},请求异常:{}", url, ExceptionUtil.stacktraceToOneLineString(e), e);
        }
        return result;
    }

    /**
     * Post请求
     *
     * @param url
     * @param data
     * @param mediaType
     * @return java.lang.String
     * @throws
     * @author wliduo[i@dolyw.com]
     * @date 2021/8/23 10:18
     */
    public String post(String url, String data, MediaType mediaType) {
        return post(url, data, mediaType, null);
    }

    /**
     * Post请求
     *
     * @param url
     * @param data
     * @param mediaType
     * @param headers
     * @return java.lang.String
     * @throws
     * @author wliduo[i@dolyw.com]
     * @date 2021/8/23 10:18
     */
    public String post(String url, String data, MediaType mediaType, Map<String, String> headers) {
        String result = StrUtil.EMPTY;
        RequestBody requestBody = RequestBody.create(mediaType, data);
        Request.Builder builder = new Request.Builder();
        if (headers != null && headers.size() > 0) {
            for (Map.Entry entry : headers.entrySet()) {
                builder.header(entry.getKey().toString(), entry.getValue().toString());
            }
        }
        Request request = builder.url(url).post(requestBody).build();
        try (Response response = okHttpClient.newCall(request).execute()) {
            result = response.body().string();
            log.info("请求地址:{},请求参数:{},请求结果:{}", url, data, result);
        } catch (IOException e) {
            log.error("请求地址:{},请求参数:{},请求异常:{}", url, data, ExceptionUtil.stacktraceToOneLineString(e), e);
        }
        return result;
    }

    /**
     * http请求
     *
     * @param url
     * @param data
     * @param request
     * @return java.lang.String
     * @throws
     * @author wliduo[i@dolyw.com]
     * @date 2022/7/14 13:40
     */
    public String http(String url, String data, HttpServletRequest request) {
        String result = StrUtil.EMPTY;
        // ContentType处理
        MediaType mediaType = MediaType.parse(request.getHeader(HttpHeaders.CONTENT_TYPE) + "; charset=utf-8");
        RequestBody requestBody = RequestBody.create(mediaType, data);
        Request.Builder builder = new Request.Builder();
        // Header处理
        Enumeration<String> headerNames =  request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            builder.header(headerName, request.getHeader(headerName));
        }
        // Method处理
        String method = request.getMethod().toUpperCase();
        Request.Builder requestBuild = builder.url(url);
        if (Method.POST.toString().equals(method)) {
            requestBuild.post(requestBody);
        }
        if (Method.PUT.toString().equals(method)) {
            requestBuild.put(requestBody);
        }
        if (Method.DELETE.toString().equals(method)) {
            requestBuild.delete(requestBody);
        }
        try (Response response = okHttpClient.newCall(requestBuild.build()).execute()) {
            result = response.body().string();
            log.info("请求地址:{},请求参数:{},请求结果:{}", url, data, result);
        } catch (IOException e) {
            log.error("请求地址:{},请求参数:{},请求异常:{}", url, data, ExceptionUtil.stacktraceToOneLineString(e), e);
        }
        return result;
    }
}

参考

上次更新时间: 2023-12-15 03:14:55