From ea32eda8f3ab4c3c34011d95c8d29e640ed5cd97 Mon Sep 17 00:00:00 2001 From: lqyan Date: Thu, 1 Feb 2024 16:17:03 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8E=B7=E5=8F=96token=E5=92=8Ctoken=E7=9A=84?= =?UTF-8?q?=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- pom.xml | 66 ++- .../com/jhinno/sdk/openapi/ArgsException.java | 84 ++++ .../jhinno/sdk/openapi/ClientErrorCode.java | 54 +++ .../jhinno/sdk/openapi/ClientException.java | 152 +++++++ .../jhinno/sdk/openapi/CommonConstant.java | 24 ++ .../jhinno/sdk/openapi/ServiceException.java | 64 +++ .../sdk/openapi/api/JHApiExecution.java | 74 ++++ .../sdk/openapi/api/ResponseResult.java | 102 +++++ .../com/jhinno/sdk/openapi/api/TokenInfo.java | 79 ++++ .../api/auth/AuthJHApiClientExecution.java | 12 + .../auth}/AuthPathConstant.java | 2 +- .../jhinno/sdk/openapi/api/auth/Token.java | 33 ++ .../client/DefaultHttpClientConfig.java | 36 ++ .../sdk/openapi/client/JHApiClient.java | 402 ++++++++++++++++++ .../jhinno/sdk/openapi/client/ResultType.java | 10 + .../jhinno/sdk/openapi/common/auth/Token.java | 8 - .../sdk/openapi/utils/HttpClientUtils.java | 8 - 18 files changed, 1178 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/jhinno/sdk/openapi/ArgsException.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/ClientErrorCode.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/ClientException.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/CommonConstant.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/ServiceException.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/api/JHApiExecution.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/api/ResponseResult.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/api/TokenInfo.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/api/auth/AuthJHApiClientExecution.java rename src/main/java/com/jhinno/sdk/openapi/{constant => api/auth}/AuthPathConstant.java (90%) create mode 100644 src/main/java/com/jhinno/sdk/openapi/api/auth/Token.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/client/DefaultHttpClientConfig.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/client/JHApiClient.java create mode 100644 src/main/java/com/jhinno/sdk/openapi/client/ResultType.java delete mode 100644 src/main/java/com/jhinno/sdk/openapi/common/auth/Token.java delete mode 100644 src/main/java/com/jhinno/sdk/openapi/utils/HttpClientUtils.java diff --git a/README.md b/README.md index 890cf1b..cad357e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ # Jhinno OpenAPI SDK for Java ---- - 针对Java的景行API SDK使Java开发人员能够轻松使用景行API接口。您可以在几分钟内开始使用Maven或下载一个zip文件。 ## 必要条件 -- Java 1.6 or later +- Java 1.8 or later - Maven ## 安装 diff --git a/pom.xml b/pom.xml index f8c1847..858231c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.jhinno jhinno-openapi-java-sdk - 6.0.0-SNAPSHOT + ${project.release.version} jar Jhinno OpenAPI SDK for Java The Jhinno OpenAPI SDK for Java used for accessing Jhinno OpenApi Service @@ -17,16 +17,52 @@ 8 8 UTF-8 + 6.0.0-SNAPSHOT + + + product + + 6.0.0 + + + + - org.projectlombok - lombok - 1.18.30 + org.apache.commons + commons-lang3 + 3.13.0 + + + + org.apache.httpcomponents + httpclient + 4.5.13 + + + + com.alibaba.fastjson2 + fastjson2 + 2.0.25 + + + + junit + junit + 4.13.2 + test + + + yuchat + http://192.168.3.101/repository/maven-jhinno/ + + + @@ -34,9 +70,9 @@ maven-compiler-plugin 3.6.2 - 1.6 - 1.6 - UTF-8 + ${maven.compiler.source} + ${maven.compiler.target} + ${project.build.sourceEncoding} @@ -49,24 +85,22 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.8 + 2.10.4 ${project.build.sourceEncoding} + ${project.build.sourceEncoding} + ${project.build.sourceEncoding} date X + + author + X + - - - package - - jar - - - diff --git a/src/main/java/com/jhinno/sdk/openapi/ArgsException.java b/src/main/java/com/jhinno/sdk/openapi/ArgsException.java new file mode 100644 index 0000000..336644d --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/ArgsException.java @@ -0,0 +1,84 @@ +package com.jhinno.sdk.openapi; + +/** + *

+ * 这个异常标识传入的SDK方法的参数错误 + *

+ * + *

+ * 通常情况下{@link ClientException}开发者额外处理它,因为它通常出现在开发的时候, + *

+ * + *

+ * 例如: 调用重命名文件的接口,要求源文件路径和新的文件名是必填的参数, + * 如果开发者在调用的过程中没填装个参数,则程序会抛出{@link ClientException} + *

+ * + * @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; + } + +} diff --git a/src/main/java/com/jhinno/sdk/openapi/ClientErrorCode.java b/src/main/java/com/jhinno/sdk/openapi/ClientErrorCode.java new file mode 100644 index 0000000..36b223e --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/ClientErrorCode.java @@ -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"; +} diff --git a/src/main/java/com/jhinno/sdk/openapi/ClientException.java b/src/main/java/com/jhinno/sdk/openapi/ClientException.java new file mode 100644 index 0000000..2ab5af9 --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/ClientException.java @@ -0,0 +1,152 @@ +package com.jhinno.sdk.openapi; + +/** + *

+ * 这个异常是客户端访问景行API时抛出的异常。 + *

+ * + *

+ * {@link ClientException}表示景行API客户端任何异常的类。 + * 一般情况下,{@link ClientException}要么发生在发送请求之前,要么发生在收到OSS服务器端的响应之后。 + * 例如:如果在尝试发送请求时网络断开,则SDK将抛出{@link ClientException}实例。 + *

+ * + *

+ * {@link ServiceException}是从景行API响应的错误代码转换而来的。 + * 例如,当请求的接口参数错误时,SDK会抛出一个{@link ServiceException}或其子类实例, + * 并带有特定的错误代码,调用者可以用特定的逻辑来处理。 + *

+ * + * @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 : "" + ); + } +} diff --git a/src/main/java/com/jhinno/sdk/openapi/CommonConstant.java b/src/main/java/com/jhinno/sdk/openapi/CommonConstant.java new file mode 100644 index 0000000..5fb3a9b --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/CommonConstant.java @@ -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; +} diff --git a/src/main/java/com/jhinno/sdk/openapi/ServiceException.java b/src/main/java/com/jhinno/sdk/openapi/ServiceException.java new file mode 100644 index 0000000..2205f75 --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/ServiceException.java @@ -0,0 +1,64 @@ +package com.jhinno.sdk.openapi; + +/** + *

+ * 这是表示任何预期或意外的景行API服务器端错误的基本异常类。 + *

+ * + *

+ * {@link ServiceException}是从景行API响应的错误代码转换而来的。 + * 例如,当请求的接口参数错误时,SDK会抛出一个{@link ServiceException}或其子类实例, + * 并带有特定的错误代码,调用者可以用特定的逻辑来处理。 + *

+ * + *

+ * 另一方面{@link ClientException}表示景行API客户端任何异常的类。 + * 一般情况下,{@link ClientException}要么发生在发送请求之前,要么发生在收到OSS服务器端的响应之后。 + * 例如,如果在尝试发送请求时网络断开,则SDK将抛出{@link ClientException}实例。 + *

+ * + *

+ * 所以一般来说,调用者只需要正确处理{@link ServiceException}, + * 因为它意味着请求被处理,但由于不同的错误而没有完全完成。 + * 异常中的错误代码是良好的诊断信息。有时这些例外是完全预料到的。 + *

+ * + * @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; + } +} diff --git a/src/main/java/com/jhinno/sdk/openapi/api/JHApiExecution.java b/src/main/java/com/jhinno/sdk/openapi/api/JHApiExecution.java new file mode 100644 index 0000000..60685b8 --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/api/JHApiExecution.java @@ -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 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 params = new HashMap<>(2); + + String url = JHApiClient.getUrl(AuthPathConstant.AUTH_TOKEN, params); + ResponseResult result = JH_API_CLIENT.get(url, new TypeReference>() { + }); + 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(); + } + + +} diff --git a/src/main/java/com/jhinno/sdk/openapi/api/ResponseResult.java b/src/main/java/com/jhinno/sdk/openapi/api/ResponseResult.java new file mode 100644 index 0000000..625f75d --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/api/ResponseResult.java @@ -0,0 +1,102 @@ +package com.jhinno.sdk.openapi.api; + +/** + * @author yanlongqi + * @date 2024/1/31 10:16 + */ +public class ResponseResult { + + + /** + * 状态码 + */ + 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; + } +} diff --git a/src/main/java/com/jhinno/sdk/openapi/api/TokenInfo.java b/src/main/java/com/jhinno/sdk/openapi/api/TokenInfo.java new file mode 100644 index 0000000..d3311e1 --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/api/TokenInfo.java @@ -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; + } +} diff --git a/src/main/java/com/jhinno/sdk/openapi/api/auth/AuthJHApiClientExecution.java b/src/main/java/com/jhinno/sdk/openapi/api/auth/AuthJHApiClientExecution.java new file mode 100644 index 0000000..45040fb --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/api/auth/AuthJHApiClientExecution.java @@ -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 { + + +} diff --git a/src/main/java/com/jhinno/sdk/openapi/constant/AuthPathConstant.java b/src/main/java/com/jhinno/sdk/openapi/api/auth/AuthPathConstant.java similarity index 90% rename from src/main/java/com/jhinno/sdk/openapi/constant/AuthPathConstant.java rename to src/main/java/com/jhinno/sdk/openapi/api/auth/AuthPathConstant.java index 3d93df0..35c3be2 100644 --- a/src/main/java/com/jhinno/sdk/openapi/constant/AuthPathConstant.java +++ b/src/main/java/com/jhinno/sdk/openapi/api/auth/AuthPathConstant.java @@ -1,4 +1,4 @@ -package com.jhinno.sdk.openapi.constant; +package com.jhinno.sdk.openapi.api.auth; /** * @author yanlongqi diff --git a/src/main/java/com/jhinno/sdk/openapi/api/auth/Token.java b/src/main/java/com/jhinno/sdk/openapi/api/auth/Token.java new file mode 100644 index 0000000..f43224b --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/api/auth/Token.java @@ -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; + } +} diff --git a/src/main/java/com/jhinno/sdk/openapi/client/DefaultHttpClientConfig.java b/src/main/java/com/jhinno/sdk/openapi/client/DefaultHttpClientConfig.java new file mode 100644 index 0000000..0654cb6 --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/client/DefaultHttpClientConfig.java @@ -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; +} diff --git a/src/main/java/com/jhinno/sdk/openapi/client/JHApiClient.java b/src/main/java/com/jhinno/sdk/openapi/client/JHApiClient.java new file mode 100644 index 0000000..5010f3b --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/client/JHApiClient.java @@ -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); + } + + /** + *

+ * 通过{@link DefaultHttpClientConfig}默认配置的最大连接数和服务每次能并行接收的请求数量构建一个JHApiClient实例 + *

+ * + * @param baseUrl 景行接口服务的基础地址 + * @return JHApiClient的实例 + */ + public static JHApiClient build(String baseUrl) { + return build(createHttpClients(DefaultHttpClientConfig.MAX_TOTAL, DefaultHttpClientConfig.MAX_PER_ROUT), baseUrl); + } + + /** + * 通过外部传入的{@link CloseableHttpClient}构建一个请求客户端 + *

+ * + * @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 registry = RegistryBuilder.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); + } + } + + /** + *

+ * 设置一个HTTP请求的配置 + *

+ * + *

+ * {@link JHApiClient } 默认只配置了 socket连接超时的时间(socketTimeout) 、连接超时的时间(connectTimeout)、 + * 请求超时的时间(connectionRequestTimeout)这三项,其默认配置在{@link DefaultHttpClientConfig}中。 + * 如果你要自定义你自己的配置,则可以通过{@link HttpClients}构建自己的RequestConfig来请求接口 + *

+ * + * @param requestConfig HTTP请求的配置 + */ + public void setRequestConfig(RequestConfig requestConfig) { + this.requestConfig = requestConfig; + } + + /** + * 原始发送请求 + * + * @param httpRequest 请求体 + * @param headers 请求头 + * @return 响应体 + */ + public HttpEntity request(HttpRequestBase httpRequest, Map 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 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 返回的数据类型 + * @return 返回的接口数据 + */ + public T request(HttpRequestBase httpRequest, Map headers, TypeReference 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 + * @return t + */ + public T get(String path, Map headers, TypeReference 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 返回数据的类型 + * @return 请求的数据 + */ + public T get(String path, TypeReference type) { + return get(path, null, type); + } + + /** + * 获的一个url + * + * @param path 请求地址 + * @param params 请求参数 + * @return 添加路径参数后的URL + */ + public static String getUrl(String path, Map params) { + if (StringUtils.isBlank(path)) { + throw new ClientException("path不能为空"); + } + if (params == null || params.isEmpty()) { + return path; + } + StringBuilder urlBuilder = new StringBuilder(path + "?"); + for (Map.Entry 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 返回的数据的类型 + * @param k body的类型 + * @return t + */ + public T post(String path, K body, Map headers, TypeReference 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 返回的数据的类型 + * @param k body的类型 + * @return 请求返回的数据 + */ + public T put(String path, K body, Map headers, TypeReference 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 返回的数据的类型 + * @param k body的类型 + * @return 请求返回的数据 + */ + public T put(String path, K body, TypeReference type) { + return put(path, body, null, type); + } + + /** + * @param path 请求地址 + * @param body 请求体 + * @param type 响应类型 + * @param 相应返回数据类型 + * @param 请求体数据类型 + * @return 响应数据 + */ + public T post(String path, K body, TypeReference type) { + return post(path, body, null, type); + } + + + /** + * 发起delete请求 + * + * @param path 请求地址 + * @param headers 请求头 + * @param type 响应类型 + * @param 响应数据类型 + * @return 响应数据 + */ + public T delete(String path, Map headers, TypeReference 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 响应数据类型 + * @return 响应数据 + */ + public T delete(String path, TypeReference type) { + return delete(path, null, type); + } +} diff --git a/src/main/java/com/jhinno/sdk/openapi/client/ResultType.java b/src/main/java/com/jhinno/sdk/openapi/client/ResultType.java new file mode 100644 index 0000000..f591f42 --- /dev/null +++ b/src/main/java/com/jhinno/sdk/openapi/client/ResultType.java @@ -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 extends TypeReference { +} diff --git a/src/main/java/com/jhinno/sdk/openapi/common/auth/Token.java b/src/main/java/com/jhinno/sdk/openapi/common/auth/Token.java deleted file mode 100644 index bd68930..0000000 --- a/src/main/java/com/jhinno/sdk/openapi/common/auth/Token.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.jhinno.sdk.openapi.common.auth; - -/** - * @author yanlongqi - * @date 2024/1/29 10:30 - */ -public class Token { -} diff --git a/src/main/java/com/jhinno/sdk/openapi/utils/HttpClientUtils.java b/src/main/java/com/jhinno/sdk/openapi/utils/HttpClientUtils.java deleted file mode 100644 index 275bce0..0000000 --- a/src/main/java/com/jhinno/sdk/openapi/utils/HttpClientUtils.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.jhinno.sdk.openapi.utils; - -/** - * @author yanlongqi - * @date 2024/1/29 10:31 - */ -public class HttpClientUtils { -}