获取token和token的缓存

This commit is contained in:
lqyan
2024-02-01 16:17:03 +08:00
parent 4f2eb26a1e
commit ea32eda8f3
18 changed files with 1178 additions and 36 deletions

View File

@@ -1,12 +1,10 @@
# Jhinno OpenAPI SDK for Java # Jhinno OpenAPI SDK for Java
---
针对Java的景行API SDK使Java开发人员能够轻松使用景行API接口。您可以在几分钟内开始使用Maven或下载一个zip文件。 针对Java的景行API SDK使Java开发人员能够轻松使用景行API接口。您可以在几分钟内开始使用Maven或下载一个zip文件。
## 必要条件 ## 必要条件
- Java 1.6 or later - Java 1.8 or later
- Maven - Maven
## 安装 ## 安装

66
pom.xml
View File

@@ -6,7 +6,7 @@
<groupId>com.jhinno</groupId> <groupId>com.jhinno</groupId>
<artifactId>jhinno-openapi-java-sdk</artifactId> <artifactId>jhinno-openapi-java-sdk</artifactId>
<version>6.0.0-SNAPSHOT</version> <version>${project.release.version}</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Jhinno OpenAPI SDK for Java</name> <name>Jhinno OpenAPI SDK for Java</name>
<description>The Jhinno OpenAPI SDK for Java used for accessing Jhinno OpenApi Service</description> <description>The Jhinno OpenAPI SDK for Java used for accessing Jhinno OpenApi Service</description>
@@ -17,16 +17,52 @@
<maven.compiler.source>8</maven.compiler.source> <maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target> <maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.release.version>6.0.0-SNAPSHOT</project.release.version>
</properties> </properties>
<profiles>
<profile>
<id>product</id>
<properties>
<project.release.version>6.0.0</project.release.version>
</properties>
</profile>
</profiles>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.apache.commons</groupId>
<artifactId>lombok</artifactId> <artifactId>commons-lang3</artifactId>
<version>1.18.30</version> <version>3.13.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.25</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<distributionManagement>
<repository>
<id>yuchat</id>
<url>http://192.168.3.101/repository/maven-jhinno/</url>
</repository>
</distributionManagement>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
@@ -34,9 +70,9 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.6.2</version> <version>3.6.2</version>
<configuration> <configuration>
<source>1.6</source> <source>${maven.compiler.source}</source>
<target>1.6</target> <target>${maven.compiler.target}</target>
<encoding>UTF-8</encoding> <encoding>${project.build.sourceEncoding}</encoding>
</configuration> </configuration>
</plugin> </plugin>
@@ -49,24 +85,22 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>2.8</version> <version>2.10.4</version>
<configuration> <configuration>
<encoding>${project.build.sourceEncoding}</encoding> <encoding>${project.build.sourceEncoding}</encoding>
<docencoding>${project.build.sourceEncoding}</docencoding>
<charset>${project.build.sourceEncoding}</charset>
<tags> <tags>
<tag> <tag>
<name>date</name> <name>date</name>
<placement>X</placement> <placement>X</placement>
</tag> </tag>
<tag>
<name>author</name>
<placement>X</placement>
</tag>
</tags> </tags>
</configuration> </configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin> </plugin>
<plugin> <plugin>

View File

@@ -0,0 +1,84 @@
package com.jhinno.sdk.openapi;
/**
* <p>
* 这个异常标识传入的SDK方法的参数错误
* </p>
*
* <p>
* 通常情况下{@link ClientException开发者额外处理它因为它通常出现在开发的时候
* </p>
*
* <p>
* 例如: 调用重命名文件的接口,要求源文件路径和新的文件名是必填的参数,
* 如果开发者在调用的过程中没填装个参数,则程序会抛出{@link ClientException
* </p>
*
* @author yanlongqi
* @date 2024/1/31 10:36
*/
public class ArgsException extends RuntimeException {
/**
* 用户名
*/
private String username;
/**
* 错误信息
*/
private String errorMessage;
/**
* 构建一个没有任何参数的异常实例
*/
public ArgsException() {
super();
}
/**
* 创建具有异常的实例
*
* @param cause 一个异常
*/
public ArgsException(Throwable cause) {
this(null, cause);
}
/**
* 创建一个包含错误消息和异常的实例
*
* @param errorMessage 错误信息
* @param cause 一个异常
*/
public ArgsException(String errorMessage, Throwable cause) {
super(null, cause);
this.errorMessage = errorMessage;
}
/**
* 创建一个包含错误消息的实例。
*
* @param errorMessage 错误信息
*/
public ArgsException(String errorMessage) {
this(errorMessage, null);
}
@Override
public String getMessage() {
return String.format("[ErrorMessage]:%s", errorMessage);
}
/**
* 获取一个错误信息
*
* @return 错误信息
*/
public String getErrorMessage() {
return errorMessage;
}
}

View File

@@ -0,0 +1,54 @@
package com.jhinno.sdk.openapi;
/**
* 客户端错误相关常亮的定义
*
* @author yanlongqi
* @date 2024/1/30 13:25
*/
public class ClientErrorCode {
/**
* 未知错误。这意味着错误不是预期的。
*/
public static final String UNKNOWN = "Unknown";
/**
* 未知主机。当引发{@link java.net.UnknownHostException}时返回此错误。
*/
public static final String UNKNOWN_HOST = "UnknownHost";
/**
* 连接超时。
*/
public static final String CONNECTION_TIMEOUT = "ConnectionTimeout";
/**
* Socket超时
*/
public static final String SOCKET_TIMEOUT = "SocketTimeout";
/**
* Socket异常
*/
public static final String SOCKET_EXCEPTION = "SocketException";
/**
* 服务器端拒绝连接。
*/
public static final String CONNECTION_REFUSED = "ConnectionRefused";
/**
* 输入流不可重复读取。
*/
public static final String NONREPEATABLE_REQUEST = "NonRepeatableRequest";
/**
* 读取输入流时线程中断。
*/
public static final String INPUTSTREAM_READING_ABORTED = "InputStreamReadingAborted";
/**
* SSL异常
*/
public static final String SSL_EXCEPTION = "SslException";
}

View File

@@ -0,0 +1,152 @@
package com.jhinno.sdk.openapi;
/**
* <p>
* 这个异常是客户端访问景行API时抛出的异常。
* </p>
*
* <p>
* {@link ClientException表示景行API客户端任何异常的类。
* 一般情况下,{@link ClientException要么发生在发送请求之前要么发生在收到OSS服务器端的响应之后。
* 例如如果在尝试发送请求时网络断开则SDK将抛出{@link ClientException实例。
* </p>
*
* <p>
* {@link ServiceException是从景行API响应的错误代码转换而来的。
* 例如当请求的接口参数错误时SDK会抛出一个@link ServiceException或其子类实例
* 并带有特定的错误代码,调用者可以用特定的逻辑来处理。
* </p>
*
* @author yanlongqi
* @date 2024/1/30 11:27
*/
public class ClientException extends RuntimeException {
/**
* 请求id
*/
private String requestId;
/**
* 错误编号
*/
private String errorCode;
/**
* 错误信息
*/
private String errorMessage;
/**
* 创建一个默认的实例
*/
public ClientException() {
super();
}
/**
* 创建一个包含错误消息的实例。
*
* @param errorMessage 错误信息
*/
public ClientException(String errorMessage) {
this(errorMessage, null);
}
/**
* 创建具有异常的实例
*
* @param cause 一个异常
*/
public ClientException(Throwable cause) {
this(null, cause);
}
/**
* 创建一个包含错误消息和异常的实例
*
* @param errorMessage 错误信息
* @param cause 一个异常
*/
public ClientException(String errorMessage, Throwable cause) {
super(null, cause);
this.errorMessage = errorMessage;
this.errorCode = ClientErrorCode.UNKNOWN;
this.requestId = ClientErrorCode.UNKNOWN;
}
/**
* 使用错误消息、错误代码、请求Id创建实例
*
* @param errorMessage 错误信息
* @param errorCode 错误编码
* @param requestId 请求id
*/
public ClientException(String errorMessage, String errorCode, String requestId) {
this(errorMessage, errorCode, requestId, null);
}
/**
* 创建包含错误消息、错误代码、请求Id和异常的实例。
*
* @param errorMessage 错误信息
* @param errorCode 错误编码
* @param requestId 请求id
* @param cause 一个异常
*/
public ClientException(String errorMessage, String errorCode, String requestId, Throwable cause) {
this(errorMessage, cause);
this.errorCode = errorCode;
this.requestId = requestId;
}
/**
* 创建包含错误消息、错误代码、异常的实例。
*
* @param errorMessage 错误信息
* @param errorCode 错误编码
* @param cause 一个异常
*/
public ClientException(String errorMessage, String errorCode, Throwable cause) {
this(errorMessage, cause);
this.errorCode = errorCode;
}
/**
* 获取请求id。
*
* @return 请求Id
*/
public String getRequestId() {
return requestId;
}
/**
* 获取错误代码。
*
* @return 错误代码
*/
public String getErrorCode() {
return errorCode;
}
/**
* 获取错误消息。
*
* @return 字符串中的错误消息
*/
public String getErrorMessage() {
return errorMessage;
}
@Override
public String getMessage() {
return String.format(
"%s\n[ErrorCode]: %s\n[RequestId]:%s",
getErrorMessage(),
errorCode != null ? errorCode : "",
requestId != null ? requestId : ""
);
}
}

View File

@@ -0,0 +1,24 @@
package com.jhinno.sdk.openapi;
/**
* @author yanlongqi
* @date 2024/1/31 10:17
*/
public class CommonConstant {
/**
* 请求成功时的标识
*/
public static final String SUCCESS = "success";
/**
* 请求失败时的标识
*/
public static final String FAILED = "failed";
/**
* 默认的token有效时间单位分钟
*/
public static final int DEFAULT_TOKEN_EFFECTIVE_TIME = 30;
}

View File

@@ -0,0 +1,64 @@
package com.jhinno.sdk.openapi;
/**
* <p>
* 这是表示任何预期或意外的景行API服务器端错误的基本异常类。
* </p>
*
* <p>
* {@link ServiceException是从景行API响应的错误代码转换而来的。
* 例如当请求的接口参数错误时SDK会抛出一个@link ServiceException或其子类实例
* 并带有特定的错误代码,调用者可以用特定的逻辑来处理。
* </p>
*
* <p>
* 另一方面{@link ClientException表示景行API客户端任何异常的类。
* 一般情况下,{@link ClientException要么发生在发送请求之前要么发生在收到OSS服务器端的响应之后。
* 例如如果在尝试发送请求时网络断开则SDK将抛出{@link ClientException实例。
* </p>
*
* <p>
* 所以一般来说,调用者只需要正确处理{@link ServiceException}
* 因为它意味着请求被处理,但由于不同的错误而没有完全完成。
* 异常中的错误代码是良好的诊断信息。有时这些例外是完全预料到的。
* </p>
*
* @author yanlongqi
* @date 2024/1/30 11:35
*/
public class ServiceException extends RuntimeException {
/**
* 错误信息
*/
private String errorMessage;
/**
* 创建一个默认的实例
*/
public ServiceException() {
super();
}
/**
* 创建一个包含错误消息的实例。
*
* @param errorMessage 错误信息
*/
public ServiceException(String errorMessage) {
this(errorMessage, null);
}
/**
* 创建一个包含错误消息和异常的实例
*
* @param errorMessage 错误信息
* @param cause 一个异常
*/
public ServiceException(String errorMessage, Throwable cause) {
super(null, cause);
this.errorMessage = errorMessage;
}
}

View File

@@ -0,0 +1,74 @@
package com.jhinno.sdk.openapi.api;
import com.alibaba.fastjson2.TypeReference;
import com.jhinno.sdk.openapi.ArgsException;
import com.jhinno.sdk.openapi.CommonConstant;
import com.jhinno.sdk.openapi.ServiceException;
import com.jhinno.sdk.openapi.api.auth.AuthPathConstant;
import com.jhinno.sdk.openapi.api.auth.Token;
import com.jhinno.sdk.openapi.client.JHApiClient;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* @author yanlongqi
* @date 2024/1/30 19:39
*/
public class JHApiExecution {
/**
* JHApiClient实例
*/
private static JHApiClient JH_API_CLIENT;
/**
* 用户令牌的缓存
*/
private static final Map<String, TokenInfo> TOKEN_INFO_MAP = new HashMap<>(10);
/**
* 设置在JHApiClient实例的实例
*
* @param jhApiClient 客户端实例
*/
public void setJHApiClient(JHApiClient jhApiClient) {
this.JH_API_CLIENT = jhApiClient;
}
/**
* 获取用户的Token
*
* @param username 用户名
* @param isForce 是否强制获取token
* @return 用户的token
*/
public String getToken(String username, boolean isForce) {
if (StringUtils.isBlank(username)) {
throw new ArgsException("用户名称不能为空!");
}
TokenInfo tokenInfo = TOKEN_INFO_MAP.get(username);
// 如果是强制获取、用户令牌为空、用户令牌过期等,则获取令牌
if (isForce || tokenInfo == null || System.currentTimeMillis() - tokenInfo.getCurrentTimestamp() <= CommonConstant.DEFAULT_TOKEN_EFFECTIVE_TIME * 60 * 1000) {
Map<String, Object> params = new HashMap<>(2);
String url = JHApiClient.getUrl(AuthPathConstant.AUTH_TOKEN, params);
ResponseResult<Token> result = JH_API_CLIENT.get(url, new TypeReference<ResponseResult<Token>>() {
});
if (StringUtils.equals(result.getResult(), CommonConstant.FAILED)) {
throw new ServiceException("获取token失败");
}
Token token = result.getData();
tokenInfo = new TokenInfo();
tokenInfo.setUserName(username);
tokenInfo.setToken(token.getToken());
tokenInfo.setCurrentTimestamp(System.currentTimeMillis());
TOKEN_INFO_MAP.put(username, tokenInfo);
}
return tokenInfo.getToken();
}
}

View File

@@ -0,0 +1,102 @@
package com.jhinno.sdk.openapi.api;
/**
* @author yanlongqi
* @date 2024/1/31 10:16
*/
public class ResponseResult<T> {
/**
* 状态码
*/
private int code;
/**
* 状态
*/
private String result;
/**
* 响应信息
*/
private String message;
/**
* 响应数据
*/
private T data;
/**
* 获取请求编号
*
* @return 请求编号
*/
public int getCode() {
return code;
}
/**
* 设置请求编号
*
* @param code 请求编号
*/
public void setCode(int code) {
this.code = code;
}
/**
* 获取请求状态
*
* @return 请求状态
*/
public String getResult() {
return result;
}
/**
* 设置请求状态
*
* @param result 请求状态
*/
public void setResult(String result) {
this.result = result;
}
/**
* 获取请求信息
*
* @return 请求信息
*/
public String getMessage() {
return message;
}
/**
* 设置请求信息
*
* @param message 请求信息
*/
public void setMessage(String message) {
this.message = message;
}
/**
* 获取请求数据
*
* @return 请求数据
*/
public T getData() {
return data;
}
/**
* 设置去就去数据
*
* @param data 请求数据
*/
public void setData(T data) {
this.data = data;
}
}

View File

@@ -0,0 +1,79 @@
package com.jhinno.sdk.openapi.api;
/**
* @author yanlongqi
* @date 2024/1/31 10:06
*/
public class TokenInfo {
/**
* 用户名
*/
private String userName;
/**
* 用户令牌
*/
private String token;
/**
* 用户申请令牌时的时间戳,用于校验令牌是否过期
*/
private long currentTimestamp;
/**
* 获取用户名
*
* @return 用户名
*/
public String getUserName() {
return userName;
}
/**
* 设置用户名
*
* @param userName 用户名
*/
public void setUserName(String userName) {
this.userName = userName;
}
/**
* 获取令牌
*
* @return 令牌
*/
public String getToken() {
return token;
}
/**
* 设置令牌
*
* @param token 令牌
*/
public void setToken(String token) {
this.token = token;
}
/**
* 获取申请令牌时的时间戳
*
* @return 时间戳
*/
public long getCurrentTimestamp() {
return currentTimestamp;
}
/**
* 设置时间戳
*
* @param currentTimestamp 时间戳
*/
public void setCurrentTimestamp(long currentTimestamp) {
this.currentTimestamp = currentTimestamp;
}
}

View File

@@ -0,0 +1,12 @@
package com.jhinno.sdk.openapi.api.auth;
import com.jhinno.sdk.openapi.api.JHApiExecution;
/**
* @author yanlongqi
* @date 2024/1/30 19:47
*/
public class AuthJHApiClientExecution extends JHApiExecution {
}

View File

@@ -1,4 +1,4 @@
package com.jhinno.sdk.openapi.constant; package com.jhinno.sdk.openapi.api.auth;
/** /**
* @author yanlongqi * @author yanlongqi

View File

@@ -0,0 +1,33 @@
package com.jhinno.sdk.openapi.api.auth;
/**
* 请求令牌,用户请求其他接口
*
* @author yanlongqi
* @date 2024/1/31 10:30
*/
public class Token {
/**
* 令牌
*/
private String token;
/**
* 获取令牌
*
* @return 令牌
*/
public String getToken() {
return token;
}
/**
* 设置令牌
*
* @param token 令牌
*/
public void setToken(String token) {
this.token = token;
}
}

View File

@@ -0,0 +1,36 @@
package com.jhinno.sdk.openapi.client;
/**
* 默认的HTTP客户端请求配置
*
* @author yanlongqi
* @date 2024/1/30 18:12
*/
public class DefaultHttpClientConfig {
/**
* 默认设置最大连接数
*/
public static int MAX_TOTAL = 200;
/**
* 默认服务每次能并行接收的请求数量
*/
public static int MAX_PER_ROUT = 20;
/**
* 默认socket连接超时的时间
*/
public static final int SOCKET_TIMEOUT = 5000;
/**
* 默认连接超时的时间
*/
public static final int CONNECT_TIMEOUT = 5000;
/**
* 默认请求超时的时间
*/
public static final int CONNECTION_REQUEST_TIMEOUT = 5000;
}

View File

@@ -0,0 +1,402 @@
package com.jhinno.sdk.openapi.client;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.jhinno.sdk.openapi.ClientErrorCode;
import com.jhinno.sdk.openapi.ClientException;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
/**
* 提供请求的工具
*
* @author yanlongqi
* @date 2024/1/29 10:31
*/
public class JHApiClient {
/**
* 基础的请求URL地址
* 如https://192.168.3.12/appform
*/
private String baseUrl;
/**
* 初始化一个JHApiClient的实例值得注意的是该实例只能内部创建
*
* @param baseUrl 景行接口服务的基础地址
* @param closeableHttpClient 可关闭的HTTP客户端
*/
private JHApiClient(CloseableHttpClient closeableHttpClient, String baseUrl) {
this.baseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
this.closeableHttpClient = closeableHttpClient;
this.requestConfig = RequestConfig.custom().setSocketTimeout(DefaultHttpClientConfig.SOCKET_TIMEOUT).setConnectTimeout(DefaultHttpClientConfig.CONNECT_TIMEOUT).setConnectionRequestTimeout(DefaultHttpClientConfig.CONNECTION_REQUEST_TIMEOUT).build();
}
/**
* HTTP的连接客户端
*/
private final CloseableHttpClient closeableHttpClient;
/**
* 每次发送请求的配置,如果该配置未进行设置则走{@link DefaultHttpClientConfig中的默认配置
*/
private RequestConfig requestConfig;
/**
* 通过最大连接数和服务每次能并行接收的请求数量构建一个JHApiClient实例
*
* @param maxTotal 最大连接数
* @param maxPerRout 服务每次能并行接收的请求数量
* @param baseUrl 景行接口服务的基础地址
* @return JHApiClient的实例
*/
public static JHApiClient build(int maxTotal, int maxPerRout, String baseUrl) {
return build(createHttpClients(maxTotal, maxPerRout), baseUrl);
}
/**
* <p>
* 通过{@link DefaultHttpClientConfig默认配置的最大连接数和服务每次能并行接收的请求数量构建一个JHApiClient实例
* </p>
*
* @param baseUrl 景行接口服务的基础地址
* @return JHApiClient的实例
*/
public static JHApiClient build(String baseUrl) {
return build(createHttpClients(DefaultHttpClientConfig.MAX_TOTAL, DefaultHttpClientConfig.MAX_PER_ROUT), baseUrl);
}
/**
* 通过外部传入的{@link CloseableHttpClient构建一个请求客户端
* <p>
*
* @param httpClient 请求连接池
* @param baseUrl 景行接口服务的基础地址
* @return JHApiClient的实例
*/
public static JHApiClient build(CloseableHttpClient httpClient, String baseUrl) {
return new JHApiClient(httpClient, baseUrl);
}
/**
* 初始化一个HTTP客户端实例
*
* @param maxTotal 设置最大连接数
* @param maxPerRoute 服务每次能并行接收的请求数量
* @return 返回一个可关闭的HTTP客户端示例
*/
public static CloseableHttpClient createHttpClients(int maxTotal, int maxPerRoute) {
SSLContextBuilder builder = new SSLContextBuilder();
try {
builder.loadTrustMaterial(null, (x509Certificates, s) -> true);
SSLConnectionSocketFactory sslref = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", new PlainConnectionSocketFactory()).register("https", sslref).build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(maxTotal);
cm.setDefaultMaxPerRoute(maxPerRoute);
return HttpClients.custom().setSSLSocketFactory(sslref).setConnectionManager(cm).setConnectionManagerShared(true).build();
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
throw new ClientException(e.getMessage(), ClientErrorCode.SSL_EXCEPTION, e);
}
}
/**
* 关闭JHApiClient实例 (释放所有资源)
* 调用JHApiClient实例的shutdown() 后JHApiClient实例不可用。
* 如果客户端不存在则不进行任何操作
*/
public void shutdown() {
try {
if (closeableHttpClient != null) {
closeableHttpClient.close();
}
} catch (IOException e) {
throw new ClientException("关闭JHApiClient失败", e);
}
}
/**
* <p>
* 设置一个HTTP请求的配置
* </p>
*
* <p>
* {@link JHApiClient 默认只配置了 socket连接超时的时间(socketTimeout) 、连接超时的时间(connectTimeout)、
* 请求超时的时间(connectionRequestTimeout)这三项,其默认配置在{@link DefaultHttpClientConfig中。
* 如果你要自定义你自己的配置,则可以通过{@link HttpClients构建自己的RequestConfig来请求接口
* </p>
*
* @param requestConfig HTTP请求的配置
*/
public void setRequestConfig(RequestConfig requestConfig) {
this.requestConfig = requestConfig;
}
/**
* 原始发送请求
*
* @param httpRequest 请求体
* @param headers 请求头
* @return 响应体
*/
public HttpEntity request(HttpRequestBase httpRequest, Map<String, String> headers) {
if (requestConfig == null) {
throw new ClientException("请配置requestConfig");
}
if (httpRequest == null) {
throw new ClientException("httpRequest不能为空");
}
httpRequest.setConfig(requestConfig);
if (headers == null) {
headers = new HashMap<>();
}
// 默认请求json数据
if (StringUtils.isBlank(headers.get("Content-type"))) {
headers.put("Content-type", "application/json");
}
// 添加请求头
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpRequest.setHeader(entry.getKey(), entry.getValue());
}
try {
HttpResponse response = closeableHttpClient.execute(httpRequest);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
httpRequest.releaseConnection();
throw new ClientException("发送HTTP请求失败");
}
return response.getEntity();
} catch (Exception e) {
throw new ClientException(e.getMessage());
}
}
/**
* 发送请求
*
* @param httpRequest HttpRequestBase
* @param headers 请求头
* @param type 返回数据类型
* @param <T> 返回的数据类型
* @return 返回的接口数据
*/
public <T> T request(HttpRequestBase httpRequest, Map<String, String> headers, TypeReference<T> type) {
try {
String result = EntityUtils.toString(request(httpRequest, headers));
return type.parseObject(result);
} catch (Exception e) {
throw new ClientException(e.getMessage());
}
}
/**
* 发送一个get请求
*
* @param path 接口地址
* @param headers 请求头
* @param type 请求返回值类型
* @param <T> t
* @return t
*/
public <T> T get(String path, Map<String, String> headers, TypeReference<T> type) {
if (StringUtils.isBlank(path)) {
throw new RuntimeException("url不能为空");
}
HttpGet httpGet = new HttpGet(baseUrl + path);
return request(httpGet, headers, type);
}
/**
* 发起一个get请求
*
* @param path 接口地址
* @param type 返回数据的类型
* @param <T> 返回数据的类型
* @return 请求的数据
*/
public <T> T get(String path, TypeReference<T> type) {
return get(path, null, type);
}
/**
* 获的一个url
*
* @param path 请求地址
* @param params 请求参数
* @return 添加路径参数后的URL
*/
public static String getUrl(String path, Map<String, Object> params) {
if (StringUtils.isBlank(path)) {
throw new ClientException("path不能为空");
}
if (params == null || params.isEmpty()) {
return path;
}
StringBuilder urlBuilder = new StringBuilder(path + "?");
for (Map.Entry<String, Object> entry : params.entrySet()) {
try {
Object value = entry.getValue();
// 如果值为空,则不添加该字段
if (value == null) {
continue;
}
urlBuilder.append(entry.getKey()).append("=");
if (value instanceof String) {
urlBuilder.append(URLEncoder.encode((String) value, "utf-8"));
} else {
urlBuilder.append(value);
}
urlBuilder.append("&");
} catch (UnsupportedEncodingException e) {
throw new ClientException("url参数编码失败", ClientErrorCode.UNKNOWN_HOST, e);
}
}
urlBuilder.setLength(urlBuilder.length() - 1);
return urlBuilder.toString();
}
/**
* 发送post请求
*
* @param path 请求地址
* @param body 请求体
* @param headers 请求头
* @param type 请求数据类型
* @param <T> t 返回的数据的类型
* @param <K> k body的类型
* @return t
*/
public <T, K> T post(String path, K body, Map<String, String> headers, TypeReference<T> type) {
if (StringUtils.isBlank(path)) {
throw new RuntimeException("path不能为空");
}
HttpPost httpPost = new HttpPost(baseUrl + path);
try {
if (body != null) {
httpPost.setEntity(new StringEntity(JSONObject.toJSONString(body), "utf-8"));
}
return request(httpPost, headers, type);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 发起put请求
*
* @param path 请求地址
* @param body 请求体
* @param headers 请求头
* @param type 请求数据类型
* @param <T> t 返回的数据的类型
* @param <K> k body的类型
* @return 请求返回的数据
*/
public <T, K> T put(String path, K body, Map<String, String> headers, TypeReference<T> type) {
if (StringUtils.isBlank(path)) {
throw new RuntimeException("url不能为空");
}
HttpPut httpPost = new HttpPut(baseUrl + path);
try {
if (body != null) {
httpPost.setEntity(new StringEntity(JSONObject.toJSONString(body), "utf-8"));
}
return request(httpPost, headers, type);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
/**
* 发起put请求
*
* @param path 请求地址
* @param body 请求体
* @param type 请求数据类型
* @param <T> t 返回的数据的类型
* @param <K> k body的类型
* @return 请求返回的数据
*/
public <T, K> T put(String path, K body, TypeReference<T> type) {
return put(path, body, null, type);
}
/**
* @param path 请求地址
* @param body 请求体
* @param type 响应类型
* @param <T> 相应返回数据类型
* @param <K> 请求体数据类型
* @return 响应数据
*/
public <T, K> T post(String path, K body, TypeReference<T> type) {
return post(path, body, null, type);
}
/**
* 发起delete请求
*
* @param path 请求地址
* @param headers 请求头
* @param type 响应类型
* @param <T> 响应数据类型
* @return 响应数据
*/
public <T> T delete(String path, Map<String, String> headers, TypeReference<T> type) {
if (StringUtils.isBlank(path)) {
throw new RuntimeException("url不能为空");
}
HttpDelete httpDelete = new HttpDelete(baseUrl + path);
return request(httpDelete, headers, type);
}
/**
* 发起delete请求
*
* @param path 请求地址
* @param type 响应类型
* @param <T> 响应数据类型
* @return 响应数据
*/
public <T> T delete(String path, TypeReference<T> type) {
return delete(path, null, type);
}
}

View File

@@ -0,0 +1,10 @@
package com.jhinno.sdk.openapi.client;
import com.alibaba.fastjson2.TypeReference;
/**
* @author yanlongqi
* @date 2024/1/30 19:35
*/
public class ResultType<T> extends TypeReference<T> {
}

View File

@@ -1,8 +0,0 @@
package com.jhinno.sdk.openapi.common.auth;
/**
* @author yanlongqi
* @date 2024/1/29 10:30
*/
public class Token {
}

View File

@@ -1,8 +0,0 @@
package com.jhinno.sdk.openapi.utils;
/**
* @author yanlongqi
* @date 2024/1/29 10:31
*/
public class HttpClientUtils {
}