├── CONTRIBUTING.md ├── admin4j-common-http-starter ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring.factories │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── admin4j │ │ │ └── http │ │ │ ├── HttpPropsConfiguration.java │ │ │ └── config │ │ │ └── HttpAutoConfiguration.java │ └── test │ │ └── java │ │ └── io │ │ └── github │ │ └── admin4j │ │ └── http │ │ └── HttpConfigurationTest.java └── pom.xml ├── admin4j-common-http ├── src │ ├── main │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── admin4j │ │ │ └── http │ │ │ ├── core │ │ │ ├── Method.java │ │ │ ├── HttpHeaderKey.java │ │ │ ├── Pair.java │ │ │ ├── HttpLogger.java │ │ │ ├── HttpCallback.java │ │ │ ├── HttpDefaultConfig.java │ │ │ ├── HttpConfig.java │ │ │ ├── MediaTypeEnum.java │ │ │ ├── AbstractHttpRequest.java │ │ │ ├── AbstractHttpExecute.java │ │ │ └── AbstractHttpBuildCall.java │ │ │ ├── exception │ │ │ └── HttpException.java │ │ │ ├── ApiClient.java │ │ │ ├── factory │ │ │ └── HttpClientFactory.java │ │ │ ├── HttpRequest.java │ │ │ ├── util │ │ │ ├── HttpJsonUtil.java │ │ │ └── HttpUtil.java │ │ │ └── ApiJsonClient.java │ └── test │ │ └── java │ │ ├── http │ │ ├── entity │ │ │ ├── R.java │ │ │ └── CsdnR.java │ │ ├── ebay │ │ │ ├── EbayClient.java │ │ │ ├── EbayInventoryClient.java │ │ │ └── EbayOrderClient.java │ │ ├── util │ │ │ ├── HttpJsonUtilTest.java │ │ │ └── HttpUtilTest.java │ │ ├── TestProxy.java │ │ ├── AbstractApiClient.java │ │ ├── ApiJsonClientTest.java │ │ └── ApiClientTest.java │ │ └── io │ │ └── github │ │ └── admin4j │ │ └── http │ │ ├── SubApiClient.java │ │ ├── ApiClientTest.java │ │ └── HttpRequestTest.java └── pom.xml ├── .gitignore ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── maven-publish.yml │ ├── maven-publish-parent.yml │ └── maven-publish-starter.yml ├── SECURITY.md ├── CODE_OF_CONDUCT.md ├── pom.xml ├── README.md └── LICENSE /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | CONTRIBUTING HTTP 2 | -------------------------------------------------------------------------------- /admin4j-common-http-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | io.github.admin4j.http.config.HttpAutoConfiguration 3 | 4 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/Method.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | /** 4 | * @author andanyang 5 | * @since 2022/4/21 11:42 6 | */ 7 | public enum Method { 8 | GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE, CONNECT, PATCH 9 | } 10 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/entity/R.java: -------------------------------------------------------------------------------- 1 | package http.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author andanyang 7 | * @since 2022/5/10 18:32 8 | */ 9 | @Data 10 | public class R { 11 | private int code; 12 | private Object data; 13 | private String msg; 14 | } -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/entity/CsdnR.java: -------------------------------------------------------------------------------- 1 | package http.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author andanyang 7 | * @since 2023/5/5 16:45 8 | */ 9 | @Data 10 | public class CsdnR { 11 | private int code; 12 | private Object data; 13 | private String message; 14 | private String traceId; 15 | } 16 | -------------------------------------------------------------------------------- /admin4j-common-http-starter/src/test/java/io/github/admin4j/http/HttpConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | /** 6 | * @author andanyang 7 | * @since 2022/11/3 16:28 8 | */ 9 | @SpringBootApplication 10 | class HttpConfigurationTest { 11 | 12 | 13 | } -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/io/github/admin4j/http/SubApiClient.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http; 2 | 3 | import io.github.admin4j.http.core.HttpConfig; 4 | 5 | /** 6 | * @author andanyang 7 | * @since 2023/8/17 15:19 8 | */ 9 | public class SubApiClient extends ApiClient { 10 | 11 | public SubApiClient(HttpConfig httpConfig) { 12 | super(httpConfig); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/HttpHeaderKey.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | /** 4 | * @author andanyang 5 | * @since 2022/5/10 16:40 6 | */ 7 | public final class HttpHeaderKey { 8 | 9 | public static final String USER_AGENT = "User-Agent"; 10 | public static final String REFERER = "Referer"; 11 | public static final String CONTENT_TYPE = "Content-Type"; 12 | } 13 | -------------------------------------------------------------------------------- /admin4j-common-http-starter/src/main/java/io/github/admin4j/http/HttpPropsConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * @author andanyang 7 | * @since 2022/11/3 16:14 8 | */ 9 | @ConfigurationProperties(prefix = "admin4j.http") 10 | public class HttpPropsConfiguration extends io.github.admin4j.http.core.HttpConfig { 11 | /** 12 | * 是否启用 13 | */ 14 | private boolean enabled = true; 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | 35 | pom-xml-flattened 36 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/io/github/admin4j/http/ApiClientTest.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http; 2 | 3 | import io.github.admin4j.http.core.HttpConfig; 4 | import okhttp3.Response; 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * @author andanyang 9 | * @since 2023/8/17 15:19 10 | */ 11 | class ApiClientTest { 12 | 13 | @Test 14 | void get() { 15 | HttpConfig httpConfig = new HttpConfig(); 16 | httpConfig.setLogName("SubApiClient.ApiClientTest"); 17 | SubApiClient client = new SubApiClient(httpConfig); 18 | Response response = client.get("https://www.baidu.com"); 19 | } 20 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/ebay/EbayClient.java: -------------------------------------------------------------------------------- 1 | package http.ebay; 2 | 3 | import io.github.admin4j.http.ApiJsonClient; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author andanyang 10 | * @since 2022/11/8 17:30 11 | */ 12 | public class EbayClient extends ApiJsonClient { 13 | 14 | /** 15 | * 店铺配置 16 | * 17 | * @param storeId 18 | */ 19 | public EbayClient(Long storeId) { 20 | 21 | //TODO 获取店铺配置 22 | Map config = new HashMap<>(); 23 | 24 | baseUrl = "https://api.ebay.com"; 25 | headerMap.put("Authorization", "Bearer " + config.get("accessToken")); 26 | headerMap.put("X-EBAY-C-MARKETPLACE-ID", config.get("marketplaceId")); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/Pair.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @author andanyang 11 | * @since 2022/5/10 11:28 12 | */ 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class Pair implements Serializable { 17 | private static final long serialVersionUID = -8127748275809834416L; 18 | private String name; 19 | private T value; 20 | 21 | public static Pair of(String name, Object value) { 22 | return new Pair(name, value); 23 | } 24 | 25 | private boolean isValidString(String arg) { 26 | if (arg == null) { 27 | return false; 28 | } 29 | if (arg.trim().isEmpty()) { 30 | return false; 31 | } 32 | 33 | return true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/ebay/EbayInventoryClient.java: -------------------------------------------------------------------------------- 1 | package http.ebay; 2 | 3 | import com.admin4j.json.mapper.JSONMapper; 4 | 5 | import java.io.IOException; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * ebay 库存相关api 11 | * 12 | * @author andanyang 13 | * @since 2022/11/8 17:34 14 | */ 15 | public class EbayInventoryClient extends EbayClient { 16 | 17 | /** 18 | * 店铺配置 19 | * 20 | * @param storeId 21 | */ 22 | public EbayInventoryClient(Long storeId) { 23 | super(storeId); 24 | } 25 | 26 | /** 27 | * 库存列表 28 | * 29 | * @param limit 30 | * @param offset 31 | * @return 32 | * @throws IOException 33 | */ 34 | public JSONMapper inventoryItem(Integer limit, Integer offset) throws IOException { 35 | 36 | Map queryMap = new HashMap(2); 37 | queryMap.put("limit", limit); 38 | queryMap.put("offset", offset); 39 | return get("/sell/inventory/v1/inventory_item", queryMap); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/util/HttpJsonUtilTest.java: -------------------------------------------------------------------------------- 1 | package http.util; 2 | 3 | import com.admin4j.json.mapper.JSONMapper; 4 | import http.entity.CsdnR; 5 | import io.github.admin4j.http.core.Pair; 6 | import io.github.admin4j.http.util.HttpJsonUtil; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author andanyang 11 | * @since 2022/11/3 13:29 12 | */ 13 | public class HttpJsonUtilTest { 14 | 15 | 16 | @Test 17 | void testGetMap() { 18 | 19 | JSONMapper stringObjectMap = HttpJsonUtil.get("https://blog.csdn.net/community/home-api/v1/get-business-list?page=2&size=20&businessType=lately&noMore=false", 20 | Pair.of("username", "agonie201218")); 21 | System.out.println("stringObjectMap = " + stringObjectMap); 22 | } 23 | 24 | @Test 25 | void testGetObject() { 26 | 27 | CsdnR csdnR = HttpJsonUtil.get("https://blog.csdn.net/community/home-api/v1/get-business-list?page=2&size=20&businessType=lately&noMore=false", 28 | CsdnR.class, 29 | Pair.of("username", "agonie201218")); 30 | System.out.println("csdnR = " + csdnR); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/ebay/EbayOrderClient.java: -------------------------------------------------------------------------------- 1 | package http.ebay; 2 | 3 | import com.admin4j.json.mapper.JSONMapper; 4 | 5 | import java.text.MessageFormat; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * 订单相关api 11 | * 12 | * @author andanyang 13 | * @since 2022/11/8 17:34 14 | */ 15 | public class EbayOrderClient extends EbayClient { 16 | 17 | 18 | /** 19 | * 店铺配置 20 | * 21 | * @param storeId 22 | */ 23 | public EbayOrderClient(Long storeId) { 24 | super(storeId); 25 | } 26 | 27 | /** 28 | * 订单列表 29 | * 30 | * @param beginTime 31 | * @param endTime 32 | * @param limit 33 | * @param offset 34 | * @return 35 | */ 36 | public JSONMapper orders(String beginTime, String endTime, int limit, int offset) { 37 | 38 | final String path = "/sell/fulfillment/v1/order"; 39 | 40 | String filter = MessageFormat.format("lastmodifieddate:[{0}..{1}]", beginTime, endTime); 41 | 42 | // 43 | Map queryMap = new HashMap<>(8); 44 | queryMap.put("filter", filter); 45 | queryMap.put("limit", limit); 46 | queryMap.put("offset", offset); 47 | 48 | return get("/sell/inventory/v1/inventory_item", queryMap); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/HttpLogger.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | import okhttp3.logging.HttpLoggingInterceptor; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.slf4j.Marker; 8 | import org.slf4j.MarkerFactory; 9 | 10 | /** 11 | * @author andanyang 12 | * @since 2022/5/10 14:24 13 | */ 14 | public class HttpLogger implements HttpLoggingInterceptor.Logger { 15 | private static final Marker MARKER = MarkerFactory.getMarker("OKHTTP"); 16 | 17 | private final Logger logger; 18 | 19 | public HttpLogger() { 20 | this.logger = LoggerFactory.getLogger(HttpLogger.class); 21 | } 22 | 23 | public HttpLogger(Logger logger) { 24 | if (logger != null) { 25 | this.logger = logger; 26 | } else { 27 | this.logger = LoggerFactory.getLogger(HttpLogger.class); 28 | } 29 | } 30 | 31 | public HttpLogger(String logName) { 32 | 33 | if (StringUtils.isBlank(logName)) { 34 | this.logger = LoggerFactory.getLogger(HttpLogger.class); 35 | } else { 36 | this.logger = LoggerFactory.getLogger(logName); 37 | } 38 | } 39 | 40 | @Override 41 | public void log(String message) { 42 | 43 | logger.debug(MARKER, message); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/maven-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path 3 | 4 | name: deployment all 5 | 6 | on: 7 | release: 8 | types: [ created ] 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Set up JDK 8 env 21 | uses: actions/setup-java@v4 22 | with: 23 | java-version: 8 24 | distribution: temurin 25 | server-id: ossrh 26 | server-username: MAVEN_USERNAME 27 | server-password: MAVEN_PASSWORD 28 | gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} 29 | gpg-passphrase: MAVEN_GPG_PASSPHRASE 30 | 31 | - name: Package with Maven 32 | run: mvn -B clean package -Dmaven.test.skip=true --file pom.xml 33 | 34 | - name: Deploy Apache Maven Central 35 | run: mvn deploy -Dmaven.test.skip=true -Possrh --file pom.xml 36 | env: 37 | MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSWORD }} 38 | MAVEN_USERNAME: ${{ secrets.OSSRH_USER }} 39 | MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 40 | GITHUB_TOKEN: ${{ github.token }} 41 | # GPG_PRIVATE_KEY: 42 | 43 | # -----BEGIN PGP PRIVATE KEY BLOCK----- 44 | # *** 45 | # -----END PGP PRIVATE KEY BLOCK----- 46 | 47 | # GPG_PASSWORD GPG_PRIVATE_KEY 的密码 48 | -------------------------------------------------------------------------------- /.github/workflows/maven-publish-parent.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path 3 | 4 | name: deployment parent 5 | 6 | on: 7 | release: 8 | types: [ created ] 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Set up JDK 8 env 21 | uses: actions/setup-java@v4 22 | with: 23 | java-version: 8 24 | distribution: temurin 25 | server-id: ossrh 26 | server-username: MAVEN_USERNAME 27 | server-password: MAVEN_PASSWORD 28 | gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} 29 | gpg-passphrase: MAVEN_GPG_PASSPHRASE 30 | 31 | - name: Package with Maven 32 | run: mvn -B clean package -Dmaven.test.skip=true --file pom.xml 33 | 34 | - name: Deploy Apache Maven Central 35 | run: mvn deploy -N -Dmaven.test.skip=true -Possrh --file pom.xml 36 | env: 37 | MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSWORD }} 38 | MAVEN_USERNAME: ${{ secrets.OSSRH_USER }} 39 | MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 40 | GITHUB_TOKEN: ${{ github.token }} 41 | # GPG_PRIVATE_KEY: 42 | 43 | # -----BEGIN PGP PRIVATE KEY BLOCK----- 44 | # *** 45 | # -----END PGP PRIVATE KEY BLOCK----- 46 | 47 | # GPG_PASSWORD GPG_PRIVATE_KEY 的密码 48 | -------------------------------------------------------------------------------- /admin4j-common-http-starter/src/main/java/io/github/admin4j/http/config/HttpAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.config; 2 | 3 | import io.github.admin4j.http.ApiClient; 4 | import io.github.admin4j.http.ApiJsonClient; 5 | import io.github.admin4j.http.HttpPropsConfiguration; 6 | import io.github.admin4j.http.core.HttpDefaultConfig; 7 | import io.github.admin4j.http.util.HttpJsonUtil; 8 | import io.github.admin4j.http.util.HttpUtil; 9 | import org.springframework.beans.factory.InitializingBean; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 12 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 13 | 14 | import javax.annotation.PostConstruct; 15 | 16 | /** 17 | * @author andanyang 18 | * @since 2022/11/3 15:27 19 | */ 20 | @EnableConfigurationProperties(HttpPropsConfiguration.class) 21 | @ConditionalOnProperty(prefix = "admin4j.http", name = "enabled", matchIfMissing = true) 22 | public class HttpAutoConfiguration implements InitializingBean { 23 | @Autowired 24 | HttpPropsConfiguration httpConfig; 25 | 26 | @PostConstruct 27 | public void initHttpClient() { 28 | 29 | HttpDefaultConfig.set(httpConfig); 30 | ApiClient apiClient = new ApiClient(httpConfig); 31 | HttpUtil.setClient(apiClient); 32 | ApiJsonClient apiJsonClient = new ApiJsonClient(httpConfig); 33 | HttpJsonUtil.setClient(apiJsonClient); 34 | } 35 | 36 | @Override 37 | public void afterPropertiesSet() throws Exception { 38 | HttpDefaultConfig.set(httpConfig); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/maven-publish-starter.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path 3 | 4 | name: http starter 5 | 6 | on: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | packages: write 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Set up JDK 8 19 | uses: actions/setup-java@v4 20 | with: 21 | java-version: 8 22 | distribution: temurin 23 | server-id: ossrh 24 | server-username: MAVEN_USERNAME 25 | server-password: MAVEN_PASSWORD 26 | gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} 27 | gpg-passphrase: MAVEN_GPG_PASSPHRASE 28 | cache: 'maven' 29 | 30 | - name: Build with Maven 31 | run: mvn -B clean package -Dmaven.test.skip=true --file admin4j-common-http-starter/pom.xml 32 | 33 | - name: Set up Apache Maven Central 34 | run: mvn deploy -Dmaven.test.skip=true -Possrh --file admin4j-common-http-starter/pom.xml 35 | env: 36 | MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSWORD }} 37 | MAVEN_USERNAME: ${{ secrets.OSSRH_USER }} 38 | MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 39 | GITHUB_TOKEN: ${{ github.token }} 40 | # GPG_PRIVATE_KEY: 41 | 42 | # -----BEGIN PGP PRIVATE KEY BLOCK----- 43 | # *** 44 | # -----END PGP PRIVATE KEY BLOCK----- 45 | 46 | # GPG_PASSWORD GPG_PRIVATE_KEY 的密码 47 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/TestProxy.java: -------------------------------------------------------------------------------- 1 | package http; 2 | 3 | import com.admin4j.json.mapper.JSONMapper; 4 | import io.github.admin4j.http.ApiJsonClient; 5 | import io.github.admin4j.http.core.HttpConfig; 6 | import io.github.admin4j.http.factory.HttpClientFactory; 7 | import okhttp3.OkHttpClient; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | /** 15 | * @author andanyang 16 | * @since 2022/11/2 17:11 17 | */ 18 | 19 | public class TestProxy { 20 | 21 | @Test 22 | void getStr() throws IOException { 23 | HttpConfig httpConfig = new HttpConfig(); 24 | 25 | HttpConfig.ProxyConfig proxyConfig = new HttpConfig.ProxyConfig(); 26 | proxyConfig.setHost("192.168.0.1"); 27 | proxyConfig.setPort(9898); 28 | proxyConfig.setUserName("ces_order"); 29 | proxyConfig.setPassword("23451Order"); 30 | httpConfig.setProxy(proxyConfig); 31 | 32 | OkHttpClient okHttpClient = HttpClientFactory.okHttpClient(httpConfig); 33 | 34 | ApiJsonClient apiClient = new ApiJsonClient(httpConfig); 35 | // apiClient.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"); 36 | // JSONObject jsonObject = apiClient.get("https://2022.ip138.com/"); 37 | 38 | Map formParams = new HashMap<>(16); 39 | formParams.put("username", "admin"); 40 | formParams.put("password", "admin123"); 41 | JSONMapper object = apiClient.get("https://api.onbuy.com/v2/auth/request-token", 42 | formParams 43 | ); 44 | System.out.println("post = " + object); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/io/github/admin4j/http/HttpRequestTest.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http; 2 | 3 | import io.github.admin4j.http.core.HttpHeaderKey; 4 | import io.github.admin4j.http.exception.HttpException; 5 | import okhttp3.Response; 6 | import org.junit.jupiter.api.Test; 7 | 8 | /** 9 | * @author andanyang 10 | * @since 2022/11/8 16:23 11 | */ 12 | class HttpRequestTest { 13 | 14 | @Test 15 | void get() { 16 | Response response = HttpRequest.get("https://search.gitee.com/?skin=rec&type=repository") 17 | .queryMap("q", "admin4j") 18 | .header(HttpHeaderKey.USER_AGENT, "admin4j") 19 | .execute(); 20 | System.out.println("response = " + response); 21 | } 22 | 23 | @Test 24 | void post() { 25 | Response response = HttpRequest.get("https://uatservice-etax.one.th/etaxbatchweb/etaxsignbatch") 26 | .queryMap("q", "admin4j") 27 | .header(HttpHeaderKey.USER_AGENT, "admin4j") 28 | .form("username", "admin") 29 | .form("password", "admin123") 30 | .execute(); 31 | System.out.println("response = " + response); 32 | } 33 | 34 | @Test 35 | void postError() { 36 | 37 | //模拟http 404 异常 38 | try{ 39 | Response response = HttpRequest.get("https://uatservice-etax.one.th/etaxbatchweb/etaxsignbatch") 40 | .queryMap("q", "admin4j") 41 | .header(HttpHeaderKey.USER_AGENT, "admin4j") 42 | .form("username", "admin") 43 | .form("password", "admin123") 44 | .execute(); 45 | System.out.println("response = " + response); 46 | }catch(HttpException e){ 47 | System.out.println("e = " + e); 48 | } 49 | 50 | } 51 | } -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/HttpCallback.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | /** 4 | * @author andanyang 5 | * @since 2022/5/10 16:22 6 | */ 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * Callback for asynchronous API call. 13 | * 14 | * @param The return type 15 | */ 16 | public interface HttpCallback { 17 | /** 18 | * This is called when the API call fails. 19 | * 20 | * @param e The exception causing the failure 21 | * @param statusCode Status code of the response if available, otherwise it would be 0 22 | * @param responseHeaders Headers of the response if available, otherwise it would be null 23 | */ 24 | void onFailure(Exception e, int statusCode, Map> responseHeaders); 25 | 26 | /** 27 | * This is called when the API call succeeded. 28 | * 29 | * @param result The result deserialized from response 30 | * @param statusCode Status code of the response 31 | * @param responseHeaders Headers of the response 32 | */ 33 | void onSuccess(T result, int statusCode, Map> responseHeaders); 34 | 35 | ///** 36 | // * This is called when the API upload processing. 37 | // * 38 | // * @param bytesWritten bytes Written 39 | // * @param contentLength content length of request body 40 | // * @param done write end 41 | // */ 42 | //void onUploadProgress(long bytesWritten, long contentLength, boolean done); 43 | // 44 | ///** 45 | // * This is called when the API downlond processing. 46 | // * 47 | // * @param bytesRead bytes Read 48 | // * @param contentLength content lenngth of the response 49 | // * @param done Read end 50 | // */ 51 | //void onDownloadProgress(long bytesRead, long contentLength, boolean done); 52 | } -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/HttpDefaultConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | import io.github.admin4j.http.factory.HttpClientFactory; 4 | import okhttp3.ConnectionPool; 5 | import okhttp3.OkHttpClient; 6 | 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * @author andanyang 11 | * @since 2022/11/3 16:32 12 | */ 13 | public class HttpDefaultConfig { 14 | 15 | /** 16 | * 默认的单列OkhttpClient 17 | */ 18 | private static OkHttpClient DEFAULT_HTTP_CLIENT; 19 | private static HttpConfig DEFAULT_CONFIG; 20 | /** 21 | * 默认的公共链接池 22 | */ 23 | private static volatile ConnectionPool DEFAULT_CONNECTIONPOOL; 24 | 25 | public static HttpConfig get() { 26 | if (DEFAULT_CONFIG == null) { 27 | DEFAULT_CONFIG = new HttpConfig(); 28 | } 29 | return DEFAULT_CONFIG; 30 | } 31 | 32 | public static void set(HttpConfig httpConfig) { 33 | DEFAULT_CONFIG = httpConfig; 34 | } 35 | 36 | public static OkHttpClient getClient() { 37 | if (DEFAULT_HTTP_CLIENT == null) { 38 | DEFAULT_HTTP_CLIENT = HttpClientFactory.okHttpClient(HttpDefaultConfig.get()); 39 | } 40 | return DEFAULT_HTTP_CLIENT; 41 | } 42 | 43 | public static void setClient(OkHttpClient httpClient) { 44 | DEFAULT_HTTP_CLIENT = httpClient; 45 | } 46 | 47 | public static ConnectionPool getConnectionPool() { 48 | if (DEFAULT_CONNECTIONPOOL == null) { 49 | synchronized (HttpDefaultConfig.class) { 50 | if (DEFAULT_CONNECTIONPOOL == null) { 51 | DEFAULT_CONNECTIONPOOL = new ConnectionPool(HttpDefaultConfig.get().getMaxIdleConnections(), HttpDefaultConfig.get().getKeepAliveDuration(), TimeUnit.SECONDS); 52 | } 53 | } 54 | } 55 | return DEFAULT_CONNECTIONPOOL; 56 | } 57 | 58 | public static void setConnectionPool(ConnectionPool connectionPool) { 59 | 60 | DEFAULT_CONNECTIONPOOL = connectionPool; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/HttpConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | import lombok.Data; 4 | import okhttp3.Protocol; 5 | import okhttp3.logging.HttpLoggingInterceptor; 6 | 7 | import java.net.Proxy; 8 | import java.util.List; 9 | 10 | /** 11 | * @author andanyang 12 | * @since 2022/4/21 11:32 13 | */ 14 | @Data 15 | public class HttpConfig { 16 | 17 | 18 | /** 19 | * 日志等级 20 | */ 21 | private HttpLoggingInterceptor.Level loggLevel = HttpLoggingInterceptor.Level.BODY; 22 | /** 23 | * 日志名称 24 | */ 25 | private String logName = ""; 26 | 27 | /** 28 | * 读取超时时间,秒 29 | */ 30 | private long readTimeout = 30; 31 | /** 32 | * 链接超时时间 33 | */ 34 | private long connectTimeout = 30; 35 | 36 | private boolean followRedirects = false; 37 | 38 | /** 39 | * 最大的连接数 40 | */ 41 | private int maxIdleConnections = 5; 42 | 43 | /** 44 | * 最大的kepAlive 时间 秒 45 | */ 46 | private long keepAliveDuration = 5; 47 | 48 | private String userAgent = "OKHTTP"; 49 | private String referer = ""; 50 | /** 51 | * 是否支持cookie 52 | */ 53 | private boolean cookie = false; 54 | /** 55 | * 是否共用连接池 56 | */ 57 | private boolean commonConnectionPool = true; 58 | 59 | /** 60 | * 代理配置 61 | */ 62 | private ProxyConfig proxy; 63 | 64 | /** 65 | * 请求协议 66 | * - HTTP_1_0: HTTP/1.0 67 | * - HTTP_1_1: HTTP/1.1 68 | * - HTTP_2: HTTP/2 69 | *

70 | * 按优先顺序排列。如果列表包含 Protocol.H2_PRIOR_KNOWLEDGE ,则该协议必须是唯一的协议,并且不支持 HTTPS URL。 71 | * 否则,列表必须包含 Protocol. HTTP_1_1。该列表不得包含 null 或 Protocol. HTTP_1_0 72 | */ 73 | private List protocols; 74 | 75 | 76 | /** 77 | * 代理配置 78 | */ 79 | @Data 80 | public static class ProxyConfig { 81 | 82 | private Proxy.Type type = Proxy.Type.HTTP; 83 | private String host; 84 | private Integer port = 80; 85 | private String userName; 86 | private String password; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/exception/HttpException.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.exception; 2 | 3 | import okhttp3.Response; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author andanyang 10 | * @since 2022/5/10 16:01 11 | */ 12 | public class HttpException extends RuntimeException { 13 | 14 | private int code = 0; 15 | private Map> responseHeaders = null; 16 | private String responseBody = null; 17 | 18 | public HttpException(Throwable throwable) { 19 | super(throwable); 20 | } 21 | 22 | public HttpException(String message) { 23 | super(message); 24 | } 25 | 26 | public HttpException(String message,Throwable throwable) { 27 | super(message,throwable); 28 | } 29 | 30 | public HttpException(String message, int code) { 31 | super(message); 32 | this.code = code; 33 | } 34 | 35 | public HttpException(String message, Throwable throwable, int code, Map> responseHeaders, String responseBody) { 36 | super(message, throwable); 37 | this.code = code; 38 | this.responseHeaders = responseHeaders; 39 | this.responseBody = responseBody; 40 | } 41 | 42 | public HttpException(String message, int code, Map> responseHeaders, String responseBody) { 43 | super(message); 44 | this.code = code; 45 | this.responseHeaders = responseHeaders; 46 | this.responseBody = responseBody; 47 | } 48 | 49 | public HttpException(String message, Throwable throwable, int code, Map> responseHeaders) { 50 | super(message, throwable); 51 | this.code = code; 52 | this.responseHeaders = responseHeaders; 53 | } 54 | 55 | public HttpException(Response response) { 56 | super(response.message()); 57 | this.code = 15000 + response.code(); 58 | this.responseHeaders = response.headers().toMultimap(); 59 | //this.responseBody = response.body().string(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/MediaTypeEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | import lombok.Getter; 4 | import okhttp3.MediaType; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | /** 8 | * @author andanyang 9 | * @Date 2021/6/2 17:21 10 | */ 11 | public enum MediaTypeEnum { 12 | /** 13 | * Content Type 为JSON 14 | */ 15 | JSON(MediaType.parse("application/json;charset=utf-8")), 16 | FORM(MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8")), 17 | FORM_DATA(MediaType.parse("multipart/form-data")), 18 | XML(MediaType.parse("text/xml;charset=UTF-8")), 19 | /** 20 | * 纯文本格式 21 | */ 22 | PLAIN(MediaType.parse("text/plain;charset=UTF-8")), 23 | HTML(MediaType.parse("text/html;charset=UTF-8")), 24 | /** 25 | * 二进制流数据(如常见的文件下载) 26 | */ 27 | OCTET_STREAM(MediaType.parse("application/octet-stream")), 28 | OTHER(null); 29 | 30 | 31 | /** 32 | * Content Type 类型 33 | */ 34 | @Getter 35 | private final MediaType mediaType; 36 | 37 | MediaTypeEnum(MediaType mediaType) { 38 | this.mediaType = mediaType; 39 | } 40 | 41 | public static MediaTypeEnum of(Object contentType) { 42 | if (contentType instanceof MediaTypeEnum) { 43 | return (MediaTypeEnum) contentType; 44 | } else if (contentType instanceof String) { 45 | String contentStr = (String) contentType; 46 | //application/json;charset=utf-8 application/json; 47 | contentType = StringUtils.substringBefore(contentStr, ";"); 48 | String type = StringUtils.substringBefore(contentStr, "/"); 49 | String subType = StringUtils.substringAfter(contentStr, "/"); 50 | for (MediaTypeEnum mediaType : MediaTypeEnum.values()) { 51 | if (mediaType.getMediaType().type().equals(type) && mediaType.getMediaType().subtype().equals(subType)) { 52 | return mediaType; 53 | } 54 | } 55 | } 56 | 57 | return MediaTypeEnum.OTHER; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return mediaType.toString(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/AbstractApiClient.java: -------------------------------------------------------------------------------- 1 | package http; 2 | 3 | import io.github.admin4j.http.ApiClient; 4 | import io.github.admin4j.http.core.HttpConfig; 5 | import io.github.admin4j.http.core.MediaTypeEnum; 6 | import io.github.admin4j.http.core.Pair; 7 | import io.github.admin4j.http.factory.HttpClientFactory; 8 | import okhttp3.OkHttpClient; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.UnsupportedEncodingException; 13 | import java.net.URLEncoder; 14 | import java.util.HashMap; 15 | 16 | class AbstractApiClient { 17 | 18 | ApiClient httpRequest; 19 | 20 | @BeforeEach 21 | void init() { 22 | 23 | HttpConfig httpConfig = new HttpConfig(); 24 | httpRequest = new ApiClient(new HttpConfig()); 25 | OkHttpClient httpClient = HttpClientFactory.okHttpClient(httpConfig); 26 | //httpRequest.setOkHttpClient(httpClient); 27 | } 28 | 29 | @Test 30 | void escapeString() throws UnsupportedEncodingException { 31 | 32 | String m = MediaTypeEnum.JSON.getMediaType().toString(); 33 | System.out.println(m); 34 | 35 | 36 | HttpConfig httpConfig = new HttpConfig(); 37 | ApiClient httpRequest = new ApiClient(httpConfig); 38 | 39 | String str = "abc=adc&acd+3"; 40 | String utf8 = URLEncoder.encode(str, "utf8"); 41 | System.out.println(utf8); 42 | String s = httpRequest.escapeString(str); 43 | System.out.println(s); 44 | } 45 | 46 | @Test 47 | void buildUrl() { 48 | 49 | Pair[] ps = new Pair[1]; 50 | ps[0] = Pair.of("name", "value"); 51 | String buildUrl = httpRequest.buildUrl("https://blog.csdn.net/", ps, null); 52 | System.out.println("buildUrl = " + buildUrl); 53 | HashMap map = new HashMap<>(16); 54 | map.put("test", "啊啊哈"); 55 | map.put("test1", "1"); 56 | map.put("test2", "2"); 57 | map.put("test3", "3"); 58 | buildUrl = httpRequest.buildUrl("https://blog.csdn.net/?", null, map); 59 | System.out.println("buildUrl = " + buildUrl); 60 | } 61 | 62 | @Test 63 | void mediaTypeEnum() { 64 | MediaTypeEnum of = MediaTypeEnum.of("application/json"); 65 | System.out.println("of = " + of); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/util/HttpUtilTest.java: -------------------------------------------------------------------------------- 1 | package http.util; 2 | 3 | import io.github.admin4j.http.core.Pair; 4 | import io.github.admin4j.http.util.HttpUtil; 5 | import okhttp3.Response; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * @author andanyang 15 | * @since 2022/11/3 13:29 16 | */ 17 | public class HttpUtilTest { 18 | 19 | 20 | @Test 21 | void get() { 22 | Response response = HttpUtil.get("https://github.com/search", Pair.of("q", "okhttp")); 23 | System.out.println("response = " + response); 24 | } 25 | 26 | @Test 27 | void testGet() { 28 | Map queryParams = new HashMap<>(); 29 | queryParams.put("q", "okhttp"); 30 | Response response = HttpUtil.get("https://search.gitee.com/", queryParams); 31 | System.out.println("response = " + response); 32 | } 33 | 34 | @Test 35 | void post() { 36 | 37 | Response post = HttpUtil.post("https://oapi.dingtalk.com/robot/send?access_token=37f5954ab60ea8b2e431ae9101b1289c138e85aa6eb6e3940c35ee13ff8b6335", "{\"msgtype\": \"text\",\"text\": {\"content\":\"【反馈提醒】我就是我, 是不一样的烟火\"}}"); 38 | System.out.println("post = " + post); 39 | } 40 | 41 | @Test 42 | void postForm() { 43 | 44 | Map formParams = new HashMap<>(16); 45 | formParams.put("username", "admin"); 46 | formParams.put("password", "admin123"); 47 | Response response = HttpUtil.postForm("http://192.168.1.13:9100/auth/login", 48 | formParams 49 | ); 50 | System.out.println("response = " + response); 51 | } 52 | 53 | @Test 54 | void upload() { 55 | File file = new File("C:\\Users\\andanyang\\Downloads\\Sql.txt"); 56 | Map formParams = new HashMap<>(); 57 | formParams.put("key", "test"); 58 | formParams.put("file", file); 59 | formParams.put("token", "WXyUseb-D4sCum-EvTIDYL-mEehwDtrSBg-Zca7t:qgOcR2gUoKmxt-VnsNb657Oatzo=:eyJzY29wZSI6InpoYW56aGkiLCJkZWFkbGluZSI6MTY2NTMxNH0="); 60 | Response response = HttpUtil.upload("https://upload.qiniup.com/", formParams); 61 | System.out.println(response); 62 | } 63 | 64 | @Test 65 | void down() throws IOException { 66 | 67 | HttpUtil.down("https://gitee.com/admin4j/common-http", "path/"); 68 | } 69 | 70 | @Test 71 | void testDown() { 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/ApiJsonClientTest.java: -------------------------------------------------------------------------------- 1 | package http; 2 | 3 | import com.admin4j.json.mapper.JSONMapper; 4 | import http.entity.R; 5 | import io.github.admin4j.http.ApiJsonClient; 6 | import io.github.admin4j.http.core.HttpConfig; 7 | import io.github.admin4j.http.exception.HttpException; 8 | import io.github.admin4j.http.util.HttpJsonUtil; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.io.IOException; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * @author andanyang 19 | * @since 2022/5/10 14:08 20 | */ 21 | class ApiJsonClientTest { 22 | ApiJsonClient httpRequest; 23 | 24 | @BeforeEach 25 | void init() { 26 | HttpConfig httpConfig = new HttpConfig(); 27 | httpRequest = new ApiJsonClient(httpConfig); 28 | } 29 | 30 | 31 | @Test 32 | void get() throws IOException { 33 | JSONMapper stringObjectMap = httpRequest.get("https://gitee.com/o0w0o_1/mock/raw/master/Map"); 34 | System.out.println("stringObjectMap = " + stringObjectMap); 35 | System.out.println("code = " + stringObjectMap.getInteger("code")); 36 | } 37 | 38 | @Test 39 | void getObject() throws IOException { 40 | R csdnR = httpRequest.get("https://gitee.com/o0w0o_1/mock/raw/master/Map", R.class); 41 | System.out.println("csdnR = " + csdnR); 42 | System.out.println("csdnR = " + csdnR.getMsg()); 43 | System.out.println("csdnR = " + csdnR.getCode()); 44 | System.out.println("csdnR = " + csdnR.getData()); 45 | 46 | 47 | } 48 | 49 | @Test 50 | void getList() throws IOException { 51 | 52 | List list = httpRequest.getList("https://gitee.com/o0w0o_1/mock/raw/master/list_object", Object.class); 53 | System.out.println("list = " + list); 54 | } 55 | 56 | //TODO - Gsog error 57 | @Test 58 | void getListObject() throws IOException { 59 | 60 | List list = httpRequest.getList("https://gitee.com/o0w0o_1/mock/raw/master/list_object", R.class); 61 | System.out.println("list = " + list); 62 | for (R r : list) { 63 | Object data = r.getData(); 64 | System.out.println("data = " + data); 65 | } 66 | } 67 | 68 | 69 | @Test 70 | void postFormError() { 71 | 72 | try { 73 | Map formParams = new HashMap<>(16); 74 | formParams.put("username", "admin"); 75 | formParams.put("password", "admin123"); 76 | JSONMapper object = httpRequest.postForm("https://uatservice-etax.one.th/etaxbatchweb/etaxsignbatc", 77 | formParams 78 | ); 79 | System.out.println("post = " + object); 80 | }catch (HttpException e){ 81 | System.out.println("e = " + e); 82 | e.printStackTrace(); 83 | } 84 | 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/ApiClient.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http; 2 | 3 | import com.admin4j.json.JSONUtil; 4 | import io.github.admin4j.http.core.*; 5 | import io.github.admin4j.http.factory.HttpClientFactory; 6 | import lombok.experimental.Accessors; 7 | import lombok.extern.slf4j.Slf4j; 8 | import okhttp3.Callback; 9 | import okhttp3.Response; 10 | import org.apache.commons.lang3.StringUtils; 11 | 12 | import java.io.InputStream; 13 | import java.util.Map; 14 | 15 | /** 16 | * @author andanyang 17 | * @since 2022/4/21 11:27 18 | */ 19 | @Accessors 20 | @Slf4j 21 | public class ApiClient extends AbstractHttpRequest { 22 | public ApiClient() { 23 | super(); 24 | } 25 | 26 | public ApiClient(HttpConfig httpConfig) { 27 | 28 | if (StringUtils.isBlank(httpConfig.getLogName())) { 29 | httpConfig.setLogName(loggerName()); 30 | } 31 | okHttpClient = HttpClientFactory.okHttpClient(httpConfig); 32 | this.followRedirects = httpConfig.isFollowRedirects(); 33 | headerMap.put(HttpHeaderKey.USER_AGENT, httpConfig.getUserAgent()); 34 | headerMap.put(HttpHeaderKey.REFERER, httpConfig.getReferer()); 35 | } 36 | 37 | protected String loggerName() { 38 | return this.getClass().getName(); 39 | } 40 | 41 | @Override 42 | public String serializeJSON(Object obj) { 43 | return JSONUtil.toJSONString(obj); 44 | } 45 | 46 | 47 | //=============== request =============== 48 | public Response get(String path, Map queryMap) { 49 | return get(path, queryMap, null); 50 | } 51 | 52 | public Response get(String path, Pair... queryParams) { 53 | return get(path, null, queryParams); 54 | } 55 | 56 | 57 | public Response postForm(String url, Map formParams) { 58 | 59 | return post(url, MediaTypeEnum.FORM, null, formParams, null); 60 | } 61 | 62 | public void asyncPostForm(String url, Map formParams, Callback callback) { 63 | 64 | asyncPost(url, MediaTypeEnum.FORM, null, formParams, null, callback); 65 | } 66 | 67 | public Response postFormData(String url, Map formParams) { 68 | 69 | return post(url, MediaTypeEnum.FORM_DATA, null, formParams, null); 70 | } 71 | 72 | public void asyncPostFormData(String url, Map formParams, Callback callback) { 73 | 74 | asyncPost(url, MediaTypeEnum.FORM_DATA, null, formParams, null, callback); 75 | } 76 | 77 | public Response post(String url, Object body) { 78 | 79 | return post(url, MediaTypeEnum.JSON, body, null, null); 80 | } 81 | 82 | public void asyncPost(String url, Object body, Callback callback) { 83 | 84 | asyncPost(url, MediaTypeEnum.JSON, body, null, null, callback); 85 | } 86 | 87 | public InputStream down(String url) { 88 | return executeByteStream(url, Method.GET, null, null, null, null, null, null); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/AbstractHttpRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import okhttp3.Call; 5 | import okhttp3.Callback; 6 | import okhttp3.OkHttpClient; 7 | import okhttp3.Response; 8 | 9 | import java.util.Map; 10 | 11 | /** 12 | * @author andanyang 13 | * @since 2022/4/21 13:28 14 | */ 15 | @Slf4j 16 | public abstract class AbstractHttpRequest extends AbstractHttpExecute { 17 | 18 | 19 | /** 20 | * 每个实例可以自带的OkHttpClient 21 | */ 22 | protected OkHttpClient okHttpClient; 23 | 24 | 25 | public AbstractHttpRequest() { 26 | 27 | HttpConfig httpConfig = HttpDefaultConfig.get(); 28 | headerMap.put(HttpHeaderKey.USER_AGENT, httpConfig.getUserAgent()); 29 | headerMap.put(HttpHeaderKey.REFERER, httpConfig.getReferer()); 30 | init(); 31 | } 32 | 33 | 34 | /** 35 | * 初始化数据 baseUrl 等 36 | */ 37 | protected void init() { 38 | 39 | } 40 | 41 | @Override 42 | public OkHttpClient getHttpClient() { 43 | return okHttpClient == null ? HttpDefaultConfig.getClient() : okHttpClient; 44 | } 45 | 46 | // ======================= GET POST =============== 47 | public Response get(String path, Map queryMap, Pair... queryParams) { 48 | return get(path, queryMap,null, queryParams); 49 | } 50 | 51 | public Response get(String path, Map queryMap, Map headerMap, Pair... queryParams) { 52 | 53 | Call call = buildGet(path, queryMap,headerMap, queryParams); 54 | return execute(call); 55 | } 56 | 57 | public Response post(String url, 58 | MediaTypeEnum mediaTypeEnum, 59 | Object body, 60 | Map formParams, 61 | Map headerParams) { 62 | 63 | Call call = buildPost(url, mediaTypeEnum, body, formParams, headerParams); 64 | return execute(call); 65 | } 66 | 67 | public Response put(String url, 68 | MediaTypeEnum mediaTypeEnum, 69 | Object body, 70 | Map formParams, 71 | Map headerParams) { 72 | 73 | Call call = buildPut(url, mediaTypeEnum, body, formParams, headerParams); 74 | return execute(call); 75 | } 76 | 77 | public Response delete(String url, 78 | MediaTypeEnum mediaTypeEnum, 79 | Object body, 80 | Map formParams, 81 | Map headerParams) { 82 | 83 | Call call = buildDelete(url, mediaTypeEnum, body, formParams, headerParams); 84 | return execute(call); 85 | } 86 | 87 | public void asyncPost(String url, 88 | MediaTypeEnum mediaTypeEnum, 89 | Object body, 90 | Map formParams, 91 | Map headerParams, 92 | Callback callback) { 93 | 94 | Call call = buildPost(url, mediaTypeEnum, body, formParams, headerParams); 95 | executeAsync(call, callback); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/AbstractHttpExecute.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | import io.github.admin4j.http.exception.HttpException; 4 | import lombok.extern.slf4j.Slf4j; 5 | import okhttp3.*; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.util.Map; 10 | import java.util.function.UnaryOperator; 11 | 12 | /** 13 | * @author andanyang 14 | * @since 2022/4/21 13:28 15 | */ 16 | @Slf4j 17 | public abstract class AbstractHttpExecute extends AbstractHttpBuildCall { 18 | 19 | /** 20 | * 是否自动304 重定向跳转 21 | */ 22 | protected boolean followRedirects = true; 23 | 24 | // ------------- execute ------------- 25 | public Response execute(Call call) throws HttpException { 26 | return execute(call, this::handleResponse); 27 | } 28 | 29 | public Response execute(Call call, UnaryOperator function) throws HttpException { 30 | try { 31 | Response response = call.execute(); 32 | response = function.apply(response); 33 | return response; 34 | } 35 | catch (IOException e) { 36 | throw new HttpException(e.getMessage(),e); 37 | } 38 | } 39 | 40 | /** 41 | * 异步执行 42 | * 43 | * @param call 44 | * @param callback 45 | */ 46 | public void executeAsync(Call call, final Callback callback) { 47 | 48 | call.enqueue(callback); 49 | } 50 | 51 | /** 52 | * 返回二进制内容,用于下载文件 53 | * 54 | * @param path 55 | * @param method 56 | * @param mediaType 57 | * @param queryParams 58 | * @param queryMap 59 | * @param body 60 | * @param formParams 61 | * @param headerParams 62 | * @return 63 | */ 64 | protected InputStream executeByteStream( 65 | String path, 66 | Method method, 67 | MediaTypeEnum mediaType, 68 | Pair[] queryParams, 69 | Map queryMap, 70 | Object body, 71 | Map formParams, 72 | Map headerParams 73 | ) { 74 | Response response = null; 75 | 76 | try { 77 | Call call = buildCall(path, method, mediaType, queryParams, queryMap, body, formParams, headerParams); 78 | response = execute(call); 79 | ResponseBody responseBody = response.body(); 80 | if (!response.isSuccessful()) { 81 | log.error("executeByteStream ERROR" + responseBody.string()); 82 | throw new HttpException(response); 83 | } else { 84 | return responseBody != null ? responseBody.byteStream() : null; 85 | } 86 | } catch (IOException var8) { 87 | log.error("executeByteStream Exception", var8); 88 | throw new HttpException(var8.getMessage()); 89 | } 90 | } 91 | 92 | protected Response handleResponse(Response response) { 93 | 94 | if (response.isSuccessful()) { 95 | return response; 96 | } else if (followRedirects && response.code() <= 399 && response.code() >= 300) { 97 | //重定向 98 | Request request = buildRedirectRequest(response); 99 | if (request == null) { 100 | throw new HttpException("not find Redirect url"); 101 | } 102 | Call call = getHttpClient().newCall(request); 103 | return execute(call); 104 | } else { 105 | return response; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/factory/HttpClientFactory.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.factory; 2 | 3 | import io.github.admin4j.http.core.HttpConfig; 4 | import io.github.admin4j.http.core.HttpDefaultConfig; 5 | import io.github.admin4j.http.core.HttpLogger; 6 | import okhttp3.ConnectionPool; 7 | import okhttp3.Credentials; 8 | import okhttp3.JavaNetCookieJar; 9 | import okhttp3.OkHttpClient; 10 | import okhttp3.logging.HttpLoggingInterceptor; 11 | import org.apache.commons.lang3.StringUtils; 12 | 13 | import java.net.CookieManager; 14 | import java.net.InetSocketAddress; 15 | import java.net.Proxy; 16 | import java.time.Duration; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | import static java.net.CookiePolicy.ACCEPT_ORIGINAL_SERVER; 20 | 21 | /** 22 | * @author andanyang 23 | * @since 2022/10/9 17:39 24 | */ 25 | public class HttpClientFactory { 26 | 27 | private HttpClientFactory() { 28 | } 29 | 30 | /** 31 | * 创建 OkHttpClient 32 | * 33 | * @param httpConfig 34 | * @return 35 | */ 36 | public static OkHttpClient okHttpClient(HttpConfig httpConfig) { 37 | return okHttpClient(httpConfig, httpConfig.isCommonConnectionPool()); 38 | } 39 | 40 | 41 | /** 42 | * 创建 OkHttpClient 43 | * 44 | * @param httpConfig 45 | * @param commonConnectionPool 是否公用连接池 46 | * @return 47 | */ 48 | public static OkHttpClient okHttpClient(HttpConfig httpConfig, boolean commonConnectionPool) { 49 | 50 | ConnectionPool connectionPool; 51 | if (commonConnectionPool) { 52 | connectionPool = HttpDefaultConfig.getConnectionPool(); 53 | } else { 54 | connectionPool = new ConnectionPool(httpConfig.getMaxIdleConnections(), httpConfig.getKeepAliveDuration(), TimeUnit.SECONDS); 55 | } 56 | 57 | HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLogger(httpConfig.getLogName())); 58 | logInterceptor.setLevel(httpConfig.getLoggLevel()); 59 | OkHttpClient.Builder builder = new OkHttpClient.Builder() 60 | .connectionPool(connectionPool) 61 | //跳转由自己控制 62 | .followRedirects(httpConfig.isFollowRedirects()) 63 | .addNetworkInterceptor(logInterceptor) 64 | .readTimeout(Duration.ofSeconds(httpConfig.getReadTimeout())) 65 | .connectTimeout(Duration.ofSeconds(httpConfig.getReadTimeout())); 66 | 67 | if (httpConfig.isCookie()) { 68 | CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER); 69 | builder.cookieJar(new JavaNetCookieJar(cookieManager)); 70 | } 71 | //proxy 72 | if (httpConfig.getProxy() != null) { 73 | builder.proxy(new Proxy(httpConfig.getProxy().getType(), 74 | new InetSocketAddress(httpConfig.getProxy().getHost(), httpConfig.getProxy().getPort()))); 75 | if (StringUtils.isNotBlank(httpConfig.getProxy().getPassword())) { 76 | builder.proxyAuthenticator((route, response) -> { 77 | 78 | //设置代理服务器账号密码 79 | String credential = Credentials.basic(httpConfig.getProxy().getUserName(), httpConfig.getProxy().getPassword()); 80 | return response.request().newBuilder() 81 | .header("Proxy-Authorization", credential) 82 | .build(); 83 | }); 84 | } 85 | } 86 | 87 | // http protocols 88 | if (httpConfig.getProtocols() != null && !httpConfig.getProtocols().isEmpty()) { 89 | builder.protocols(httpConfig.getProtocols()); 90 | } 91 | 92 | return builder.build(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /admin4j-common-http-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | common-http 7 | io.github.admin4j 8 | ${revision} 9 | 10 | 4.0.0 11 | 12 | 13 | common-http-starter 14 | https://github.com/andanyoung 15 | jar 16 | common http spring starter 17 | 18 | 19 | 8 20 | 8 21 | UTF-8 22 | 1.8 23 | 2.7.13 24 | 25 | 26 | 27 | 28 | io.github.admin4j 29 | http 30 | ${revision} 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-autoconfigure 35 | provided 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-autoconfigure-processor 41 | provided 42 | true 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-configuration-processor 47 | provided 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-test 52 | test 53 | 54 | 55 | com.vaadin.external.google 56 | android-json 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-dependencies 68 | ${spring-boot.version} 69 | pom 70 | import 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-compiler-plugin 80 | 81 | utf-8 82 | 8 83 | 8 84 | 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-source-plugin 90 | 2.2.1 91 | 92 | 93 | attach-sources 94 | 95 | jar-no-fork 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | The Apache Software License, Version 2.0 107 | http://www.apache.org/licenses/LICENSE-2.0.txt 108 | repo 109 | 110 | 111 | 112 | 113 | scm:git@github.com:admin4j/common-http.git 114 | scm:git@github.com:admin4j/common-http.git 115 | 116 | https://github.com/admin4j/common-http 117 | 118 | 119 | 120 | 121 | admin4j 122 | 1218853253@qq.com 123 | https://github.com/andanyoung 124 | +8 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /admin4j-common-http/src/test/java/http/ApiClientTest.java: -------------------------------------------------------------------------------- 1 | package http; 2 | 3 | import com.admin4j.json.mapper.JSONMapper; 4 | import http.entity.R; 5 | import io.github.admin4j.http.ApiClient; 6 | import io.github.admin4j.http.core.HttpConfig; 7 | import io.github.admin4j.http.core.Method; 8 | import io.github.admin4j.http.core.Pair; 9 | import io.github.admin4j.http.exception.HttpException; 10 | import io.github.admin4j.http.util.HttpJsonUtil; 11 | import io.github.admin4j.http.util.HttpUtil; 12 | import lombok.SneakyThrows; 13 | import okhttp3.Call; 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.Test; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | /** 23 | * @author andanyang 24 | * @since 2022/5/10 14:08 25 | */ 26 | class ApiClientTest { 27 | ApiClient httpRequest; 28 | 29 | @BeforeEach 30 | void init() { 31 | HttpConfig httpConfig = new HttpConfig(); 32 | httpRequest = new ApiClient(httpConfig); 33 | } 34 | 35 | 36 | @Test 37 | void get() throws IOException { 38 | 39 | JSONMapper object = HttpJsonUtil.get("https://search.gitee.com/search/kit/lang", 40 | Pair.of("q", "admin4j"), 41 | Pair.of("type", "repository"), 42 | Pair.of("username", "agonie201218")); 43 | System.out.println("object = " + object); 44 | 45 | //object = HttpJsonUtil.get("http://192.168.1.13:9100/system/dict/i18n", 46 | // Pair.of("id", "介绍"), 47 | // Pair.of("username", "agonie201218")); 48 | //System.out.println("s1 = " + object); 49 | 50 | } 51 | 52 | @Test 53 | void post() { 54 | JSONMapper post = HttpJsonUtil.post("https://oapi.dingtalk.com/robot/send?access_token=37f5954ab60ea8b2e431ae9101b1289c138e85aa6eb6e3940c35ee13ff8b6335", 55 | "{\"msgtype\": \"text\",\"text\": {\"content\":\"【反馈提醒】我就是我, 是不一样的烟火\"}}"); 56 | System.out.println("post = " + post); 57 | } 58 | 59 | @Test 60 | void postForm() { 61 | 62 | Map formParams = new HashMap<>(16); 63 | formParams.put("username", "admin"); 64 | formParams.put("password", "admin123"); 65 | JSONMapper object = HttpJsonUtil.postForm("http://192.168.1.13:9100/auth/login", 66 | formParams 67 | ); 68 | System.out.println("post = " + object); 69 | } 70 | 71 | @Test 72 | void postForm2() { 73 | 74 | Map formParams = new HashMap<>(16); 75 | formParams.put("username", "admin"); 76 | formParams.put("password", "admin123"); 77 | R post = HttpJsonUtil.postForm("http://192.168.1.13:9100/auth/login", 78 | formParams, 79 | R.class 80 | ); 81 | System.out.println("post = " + post); 82 | } 83 | 84 | @SneakyThrows 85 | @Test 86 | void getRouters() { 87 | Map headerParams = new HashMap<>(16); 88 | headerParams.put("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NjU0NzU5MDAsInVzZXJJZCI6MX0.tcfGlq8kMfdM9RenGIfgd44osEzPslLX4dAaY9i16vc"); 89 | String url = "http://192.168.1.13:9100/system/menu/getRouters"; 90 | Call call = httpRequest.buildCall(url, Method.GET, null, null, headerParams); 91 | //Object execute = httpRequest.execute(call, R.class); 92 | //System.out.println(execute); 93 | } 94 | 95 | 96 | // 97 | ////upload/1/test/1523977637482352642.jpg 98 | // 99 | @SneakyThrows 100 | @Test 101 | void download() { 102 | // String url = "http://192.168.1.12:9100/system/upload/test"; 103 | 104 | String url = "https://img-blog.csdnimg.cn/20200709161622641.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Fnb25pZTIwMTIxOA==,size_16,color_FFFFFF,t_70"; 105 | HttpUtil.down(url, "C:\\Users\\andanyang\\Downloads\\"); 106 | } 107 | 108 | // 109 | @Test 110 | void upload() throws IOException { 111 | 112 | File file = new File("C:\\Users\\andanyang\\Downloads\\Sql.txt"); 113 | Map formParams = new HashMap<>(); 114 | formParams.put("key", "test"); 115 | formParams.put("file", "file"); 116 | formParams.put("token", "WXyUseb-D4sCum-EvTIDYL-mEehwDtrSBg-Zca7t:qgOcR2gUoKmxt-VnsNb657Oatzo=:eyJzY29wZSI6InpoYW56aGkiLCJkZWFkbGluZSI6MTY2NTMwNzUxNH0="); 117 | JSONMapper xx = HttpJsonUtil.upload("https://upload.qiniup.com/", formParams); 118 | System.out.println(xx); 119 | } 120 | 121 | @Test 122 | void postFormError() { 123 | 124 | try { 125 | Map formParams = new HashMap<>(16); 126 | formParams.put("username", "admin"); 127 | formParams.put("password", "admin123"); 128 | httpRequest.postForm("https://uatservice-etax.one.th/etaxbatchweb/etaxsignbatc", 129 | formParams 130 | ); 131 | //System.out.println("post = " + object); 132 | }catch (HttpException e){ 133 | System.out.println("e = " + e.getMessage()); 134 | e.printStackTrace(); 135 | } 136 | 137 | } 138 | } -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/HttpRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http; 2 | 3 | import io.github.admin4j.http.core.HttpHeaderKey; 4 | import io.github.admin4j.http.core.MediaTypeEnum; 5 | import io.github.admin4j.http.core.Method; 6 | import io.github.admin4j.http.core.Pair; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | import lombok.experimental.Accessors; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | /** 15 | * @author andanyang 16 | * @since 2022/10/14 15:43 17 | */ 18 | @Getter 19 | public class HttpRequest { 20 | 21 | private Method method; 22 | private MediaTypeEnum mediaType; 23 | private Map queryMap; 24 | private Pair[] queryParams; 25 | private String url; 26 | @Setter 27 | @Accessors 28 | private Map form; 29 | 30 | private Map headers; 31 | @Setter 32 | @Accessors 33 | private Object body; 34 | 35 | public static HttpRequest get(String url) { 36 | 37 | HttpRequest httpRequest = new HttpRequest(); 38 | httpRequest.url = url; 39 | httpRequest.method = Method.GET; 40 | return httpRequest; 41 | } 42 | 43 | public static HttpRequest post(String url) { 44 | 45 | HttpRequest httpRequest = new HttpRequest(); 46 | httpRequest.url = url; 47 | httpRequest.method = Method.POST; 48 | return httpRequest; 49 | } 50 | 51 | public static HttpRequest put(String url) { 52 | 53 | HttpRequest httpRequest = new HttpRequest(); 54 | httpRequest.url = url; 55 | httpRequest.method = Method.PUT; 56 | return httpRequest; 57 | } 58 | 59 | public static HttpRequest delete(String url) { 60 | 61 | HttpRequest httpRequest = new HttpRequest(); 62 | httpRequest.url = url; 63 | httpRequest.method = Method.DELETE; 64 | return httpRequest; 65 | } 66 | 67 | public static HttpRequest patch(String url) { 68 | 69 | HttpRequest httpRequest = new HttpRequest(); 70 | httpRequest.url = url; 71 | httpRequest.method = Method.PATCH; 72 | return httpRequest; 73 | } 74 | 75 | /** 76 | * 设置请求方法 77 | * 78 | * @param method 79 | * @return 80 | */ 81 | public HttpRequest method(Method method) { 82 | this.method = method; 83 | return this; 84 | } 85 | 86 | public HttpRequest mediaType(MediaTypeEnum mediaType) { 87 | this.mediaType = mediaType; 88 | return this; 89 | } 90 | 91 | public HttpRequest userAgent(String userAgent) { 92 | if (headers == null) { 93 | headers = new HashMap<>(8); 94 | } 95 | this.headers.put(HttpHeaderKey.USER_AGENT, userAgent); 96 | return this; 97 | } 98 | 99 | public HttpRequest referer(String referer) { 100 | if (headers == null) { 101 | headers = new HashMap<>(8); 102 | } 103 | this.headers.put(HttpHeaderKey.REFERER, referer); 104 | return this; 105 | } 106 | 107 | public HttpRequest header(String key, String value) { 108 | if (headers == null) { 109 | headers = new HashMap<>(8); 110 | } 111 | this.headers.put(key, value); 112 | return this; 113 | } 114 | 115 | public HttpRequest headers(Map headers) { 116 | 117 | if (headers == null) { 118 | headers = new HashMap<>(8); 119 | } 120 | this.headers = headers; 121 | return this; 122 | } 123 | 124 | 125 | public HttpRequest queryMap(String key, String value) { 126 | 127 | if (queryMap == null) { 128 | queryMap = new HashMap<>(8); 129 | } 130 | this.queryMap.put(key, value); 131 | return this; 132 | } 133 | 134 | public HttpRequest queryParams(Pair... queryParams) { 135 | 136 | this.queryParams = queryParams; 137 | return this; 138 | } 139 | 140 | public HttpRequest form(String key, Object value) { 141 | 142 | this.mediaType = MediaTypeEnum.FORM; 143 | if (form == null) { 144 | form = new HashMap<>(8); 145 | } 146 | form.put(key, value); 147 | return this; 148 | } 149 | 150 | public HttpRequest body(Object body) { 151 | 152 | this.mediaType = MediaTypeEnum.JSON; 153 | this.body = body; 154 | return this; 155 | } 156 | 157 | ////============= 发送http 请求 ================= 158 | // 159 | //public Response execute() { 160 | // 161 | // ApiClient apiClient = HttpUtil.getClient(); 162 | // Call call = apiClient.buildCall(url, method, mediaType, queryParams, queryMap, body, form, headers); 163 | // return apiClient.execute(call); 164 | //} 165 | // 166 | //public void asyncExecute(Callback callback) { 167 | // 168 | // ApiClient apiClient = HttpUtil.getClient(); 169 | // Call call = apiClient.buildCall(url, method, mediaType, queryParams, queryMap, body, form, headers); 170 | // apiClient.executeAsync(call, callback); 171 | //} 172 | // 173 | //public T execute(Class clas) throws IOException { 174 | // 175 | // @Cleanup InputStream is = Objects.requireNonNull(execute().body()).byteStream(); 176 | // return JSONUtil.parseObject(is, clas); 177 | //} 178 | } 179 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /admin4j-common-http/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | common-http 8 | io.github.admin4j 9 | ${revision} 10 | 11 | 12 | jar 13 | http 14 | 15 | http 16 | http 17 | https://github.com/andanyoung 18 | 19 | 20 | 4.11.0 21 | 3.2.0 22 | 23 | 24 | 25 | 26 | com.admin4j.json 27 | admin4j-json-fastjson2 28 | 29 | 30 | org.slf4j 31 | slf4j-api 32 | 1.7.36 33 | 34 | 35 | 36 | com.squareup.okhttp3 37 | okhttp 38 | 39 | 40 | com.squareup.okhttp3 41 | okhttp-urlconnection 42 | 43 | 44 | com.squareup.okhttp3 45 | logging-interceptor 46 | 47 | 48 | org.apache.commons 49 | commons-lang3 50 | 51 | 52 | 53 | ch.qos.logback 54 | logback-classic 55 | 1.2.11 56 | test 57 | 58 | 59 | 60 | org.projectlombok 61 | lombok 62 | provided 63 | true 64 | 65 | 66 | junit 67 | junit 68 | 4.13.2 69 | test 70 | 71 | 72 | org.junit.jupiter 73 | junit-jupiter 74 | 5.8.2 75 | test 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | com.squareup.okhttp3 84 | okhttp-bom 85 | ${okhttp.version} 86 | pom 87 | import 88 | 89 | 90 | org.apache.commons 91 | commons-lang3 92 | 3.12.0 93 | 94 | 95 | org.projectlombok 96 | lombok 97 | provided 98 | 1.18.24 99 | 100 | 101 | com.admin4j.json 102 | admin4j-json-fastjson2 103 | 0.10.0 104 | 105 | 106 | com.squareup.okio 107 | okio 108 | ${okhttp.okio.version} 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-compiler-plugin 118 | 3.10.1 119 | 120 | utf-8 121 | 8 122 | 8 123 | 124 | 125 | 126 | 127 | org.apache.maven.plugins 128 | maven-source-plugin 129 | 2.2.1 130 | 131 | 132 | attach-sources 133 | 134 | jar-no-fork 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | The Apache Software License, Version 2.0 148 | http://www.apache.org/licenses/LICENSE-2.0.txt 149 | repo 150 | 151 | 152 | 153 | 154 | scm:git@github.com:admin4j/common-http.git 155 | scm:git@github.com:admin4j/common-http.git 156 | 157 | https://github.com/admin4j/common-http 158 | 159 | 160 | 161 | 162 | admin4j 163 | 1218853253@qq.com 164 | https://github.com/andanyoung 165 | +8 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/util/HttpJsonUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.util; 2 | 3 | import com.admin4j.json.mapper.JSONMapper; 4 | import io.github.admin4j.http.ApiJsonClient; 5 | import io.github.admin4j.http.HttpRequest; 6 | import io.github.admin4j.http.core.HttpCallback; 7 | import io.github.admin4j.http.core.HttpDefaultConfig; 8 | import io.github.admin4j.http.core.Pair; 9 | import okhttp3.Call; 10 | 11 | import java.util.Map; 12 | 13 | /** 14 | * 返回body为json格式的请求工具类 15 | * 16 | * @author andanyang 17 | * @since 2022/5/10 14:31 18 | */ 19 | public class HttpJsonUtil { 20 | /** 21 | * 单列 22 | */ 23 | private static volatile ApiJsonClient SINGLETON_REQUEST = null; 24 | 25 | private HttpJsonUtil() { 26 | 27 | } 28 | 29 | public static void setClient(ApiJsonClient apiJsonClient) { 30 | SINGLETON_REQUEST = apiJsonClient; 31 | } 32 | 33 | private static ApiJsonClient getHttpRequest() { 34 | if (null == SINGLETON_REQUEST) { 35 | 36 | synchronized (HttpJsonUtil.class) { 37 | if (null == SINGLETON_REQUEST) { 38 | SINGLETON_REQUEST = new ApiJsonClient(HttpDefaultConfig.get()); 39 | } 40 | } 41 | } 42 | return SINGLETON_REQUEST; 43 | } 44 | 45 | public static JSONMapper get(String url, Pair... queryParams) { 46 | 47 | return getHttpRequest().get(url, queryParams); 48 | } 49 | 50 | public static T get(String url, Class tClass, Pair... queryParams) { 51 | 52 | return getHttpRequest().get(url, tClass, queryParams); 53 | } 54 | 55 | public static JSONMapper get(String url, Map queryParams) { 56 | 57 | return getHttpRequest().get(url, queryParams); 58 | } 59 | 60 | public static JSONMapper get(String url, Map queryParams, Map headerMap) { 61 | 62 | return getHttpRequest().get(url, queryParams, headerMap); 63 | } 64 | 65 | public static T get(String url, Class tClass, Map queryParams) { 66 | 67 | return getHttpRequest().get(url, queryParams, tClass); 68 | } 69 | 70 | /** 71 | * 发送GET 请求 72 | * 73 | * @param url 地址 74 | * @param tClass 需要JSON解析的model 75 | * @param queryParams 查询参数 76 | * @param headerMap header 参数 77 | * @param 78 | * @return 返回结果 model 79 | */ 80 | public static T get(String url, Class tClass, Map queryParams, Map headerMap) { 81 | 82 | return getHttpRequest().get(url, queryParams, headerMap, tClass); 83 | } 84 | 85 | 86 | public static JSONMapper post(String url, Object body) { 87 | return getHttpRequest().post(url, body); 88 | } 89 | 90 | public static JSONMapper post(String url, Object body, Map headerParams) { 91 | return getHttpRequest().post(url, body, headerParams); 92 | } 93 | 94 | public static T post(String url, Object body, Class tClass) { 95 | return getHttpRequest().post(url, body, tClass); 96 | } 97 | 98 | public static T post(String url, Object body, Map headerParams, Class tClass) { 99 | return getHttpRequest().post(url, body, headerParams, tClass); 100 | } 101 | 102 | public static JSONMapper postForm(String url, Map formParams) { 103 | return getHttpRequest().postForm(url, formParams); 104 | } 105 | 106 | public static JSONMapper postForm(String url, Map formParams, Map headerParams) { 107 | return getHttpRequest().postForm(url, formParams, headerParams); 108 | } 109 | 110 | public static T postForm(String url, Map formParams, Class tClass) { 111 | return getHttpRequest().postForm(url, formParams, tClass); 112 | } 113 | 114 | public static T postForm(String url, Map formParams, Map headerParams, Class tClass) { 115 | return getHttpRequest().postForm(url, formParams, headerParams, tClass); 116 | } 117 | 118 | //=================== Put ================= 119 | public static JSONMapper put(String url, Object body) { 120 | return getHttpRequest().put(url, body); 121 | } 122 | 123 | public static JSONMapper put(String url, Object body, Map headerParams) { 124 | return getHttpRequest().put(url, body, headerParams); 125 | } 126 | 127 | public static T put(String url, Object body, Class tClass) { 128 | return getHttpRequest().put(url, body, tClass); 129 | } 130 | 131 | public static T put(String url, Object body, Map headerParams, Class tClass) { 132 | return getHttpRequest().put(url, body, headerParams, tClass); 133 | } 134 | 135 | public static JSONMapper putForm(String url, Map formParams) { 136 | return getHttpRequest().putForm(url, formParams); 137 | } 138 | 139 | public static JSONMapper putForm(String url, Map formParams, Map headerParams) { 140 | return getHttpRequest().putForm(url, formParams, headerParams); 141 | } 142 | 143 | public static T putForm(String url, Map formParams, Class tClass) { 144 | return getHttpRequest().putForm(url, formParams, tClass); 145 | } 146 | 147 | public static T putForm(String url, Map formParams, Map headerParams, Class tClass) { 148 | return getHttpRequest().putForm(url, formParams, headerParams, tClass); 149 | } 150 | 151 | 152 | public static JSONMapper upload(String url, Map formParams) { 153 | return getHttpRequest().postFormData(url, formParams); 154 | } 155 | 156 | public static JSONMapper upload(String url, Map formParams, Map headerParams) { 157 | return getHttpRequest().postFormData(url, formParams, headerParams); 158 | } 159 | 160 | /** 161 | * 使用 put 方法上传 162 | * 163 | * @param url 164 | * @param formParams 165 | * @return 166 | */ 167 | public static JSONMapper uploadPut(String url, Map formParams) { 168 | return getHttpRequest().putFormData(url, formParams); 169 | } 170 | 171 | public static JSONMapper uploadPut(String url, Map formParams, Map headerParams) { 172 | return getHttpRequest().putFormData(url, formParams, headerParams); 173 | } 174 | 175 | // ============= delete ======= 176 | 177 | /** 178 | * 删除 179 | * 180 | * @param url url 181 | * @param body 如果不为空 以 JSON 格式发送 182 | * @param formParams 如果不为空 以 form 格式发送 183 | * @return JSONMapper 184 | */ 185 | public static JSONMapper delete(String url, 186 | Object body, 187 | Map formParams) { 188 | return getHttpRequest().delete(url, body, formParams); 189 | } 190 | 191 | public static JSONMapper delete(String url, 192 | Object body, 193 | Map formParams, Map headerParams) { 194 | return getHttpRequest().delete(url, body, formParams, headerParams); 195 | } 196 | 197 | /** 198 | * 删除 199 | * 200 | * @param url url 201 | * @param body 如果不为空 以 JSON 格式发送 202 | * @param formParams 如果不为空 以 form 格式发送 203 | * @return class 204 | */ 205 | public static T delete(String url, 206 | Object body, 207 | Map formParams, Class tClass) { 208 | return getHttpRequest().delete(url, body, formParams, tClass); 209 | } 210 | 211 | public static T delete(String url, 212 | Object body, 213 | Map formParams, Map headerParams, Class tClass) { 214 | return getHttpRequest().delete(url, body, formParams, headerParams, tClass); 215 | } 216 | 217 | 218 | /** 219 | * 同步执行 220 | * 221 | * @param httpRequest 222 | * @return 223 | */ 224 | public static T send(HttpRequest httpRequest, Class tClass) { 225 | 226 | ApiJsonClient apiJsonClient = HttpJsonUtil.getHttpRequest(); 227 | Call call = apiJsonClient.buildCall(httpRequest.getUrl(), httpRequest.getMethod(), httpRequest.getMediaType(), 228 | httpRequest.getQueryParams(), httpRequest.getQueryMap(), httpRequest.getBody(), httpRequest.getForm(), 229 | httpRequest.getHeaders()); 230 | return apiJsonClient.execute(call, tClass); 231 | } 232 | 233 | 234 | /** 235 | * 异步执行 236 | * 237 | * @param httpRequest 238 | * @param httpCallback 239 | */ 240 | public static void asyncExecute(HttpRequest httpRequest, Class tClass, HttpCallback httpCallback) { 241 | 242 | ApiJsonClient apiJsonClient = HttpJsonUtil.getHttpRequest(); 243 | Call call = apiJsonClient.buildCall(httpRequest.getUrl(), httpRequest.getMethod(), httpRequest.getMediaType(), 244 | httpRequest.getQueryParams(), httpRequest.getQueryMap(), httpRequest.getBody(), httpRequest.getForm(), 245 | httpRequest.getHeaders()); 246 | apiJsonClient.executeAsync(call, tClass, httpCallback); 247 | } 248 | 249 | } 250 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/util/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.util; 2 | 3 | import io.github.admin4j.http.ApiClient; 4 | import io.github.admin4j.http.HttpRequest; 5 | import io.github.admin4j.http.core.HttpDefaultConfig; 6 | import io.github.admin4j.http.core.MediaTypeEnum; 7 | import io.github.admin4j.http.core.Pair; 8 | import lombok.Cleanup; 9 | import okhttp3.Call; 10 | import okhttp3.Callback; 11 | import okhttp3.Response; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | import java.io.*; 15 | import java.util.Map; 16 | 17 | /** 18 | * HTTP 返回body为json格式的请求工具类 19 | * 20 | * @author andanyang 21 | * @since 2022/5/10 14:31 22 | */ 23 | public class HttpUtil { 24 | /** 25 | * 单列 26 | */ 27 | private static volatile ApiClient SINGLETON_REQUEST = null; 28 | 29 | private HttpUtil() { 30 | 31 | } 32 | 33 | public static ApiClient getClient() { 34 | if (null == SINGLETON_REQUEST) { 35 | 36 | synchronized (HttpUtil.class) { 37 | if (null == SINGLETON_REQUEST) { 38 | SINGLETON_REQUEST = new ApiClient(HttpDefaultConfig.get()); 39 | } 40 | } 41 | } 42 | return SINGLETON_REQUEST; 43 | } 44 | 45 | public static void setClient(ApiClient apiJsonClient) { 46 | SINGLETON_REQUEST = apiJsonClient; 47 | } 48 | 49 | /** 50 | * get 请求 51 | * 52 | * @param url 53 | * @param queryParams 查询参数 54 | * @return 55 | */ 56 | public static Response get(String url, Pair... queryParams) { 57 | 58 | return getClient().get(url, (Map) null, queryParams); 59 | } 60 | 61 | 62 | /** 63 | * get 请求 64 | * 65 | * @param url 66 | * @param queryParams 查询参数 67 | * @return 68 | */ 69 | public static Response get(String url, Map queryParams) { 70 | 71 | return getClient().get(url, queryParams, (Pair[]) null); 72 | } 73 | 74 | 75 | /** 76 | * post 请求 77 | * 78 | * @param url 79 | * @param body post body 体 80 | * @return 81 | */ 82 | public static Response post(String url, Object body) { 83 | return getClient().post(url, MediaTypeEnum.JSON, body, (Map) null, (Map) null); 84 | } 85 | 86 | /** 87 | * post(application/json;) 请求 88 | * 89 | * @param url 90 | * @param body post body 体 91 | * @param header header 头 92 | * @return 93 | */ 94 | public static Response post(String url, Object body, Map header) { 95 | return getClient().post(url, MediaTypeEnum.JSON, body, (Map) null, header); 96 | } 97 | 98 | /** 99 | * form(x-www-form-urlencoded) 格式的 post 请求 100 | * 101 | * @param url 102 | * @param formParams 103 | * @return 104 | */ 105 | public static Response postForm(String url, Map formParams) { 106 | return getClient().post(url, MediaTypeEnum.FORM, null, formParams, (Map) null); 107 | } 108 | 109 | /** 110 | * form(x-www-form-urlencoded) 格式的 post 请求 111 | * 112 | * @param url 113 | * @param formParams 114 | * @param header 115 | * @return 116 | */ 117 | public static Response postForm(String url, Map formParams, Map header) { 118 | return getClient().post(url, MediaTypeEnum.FORM, null, formParams, header); 119 | } 120 | 121 | /** 122 | * put 请求 123 | * 124 | * @param url 125 | * @param body 126 | * @return 127 | */ 128 | public static Response put(String url, Object body) { 129 | return getClient().put(url, MediaTypeEnum.JSON, body, (Map) null, (Map) null); 130 | } 131 | 132 | /** 133 | * put 请求 134 | * 135 | * @param url 136 | * @param body 137 | * @param header 138 | * @return 139 | */ 140 | public static Response put(String url, Object body, Map header) { 141 | return getClient().put(url, MediaTypeEnum.JSON, body, (Map) null, header); 142 | } 143 | 144 | /** 145 | * form(x-www-form-urlencoded) 格式的 put 请求 146 | * 147 | * @param url 148 | * @param formParams 149 | * @return 150 | */ 151 | public static Response putForm(String url, Map formParams) { 152 | return getClient().put(url, MediaTypeEnum.FORM, null, formParams, (Map) null); 153 | } 154 | 155 | /** 156 | * form(x-www-form-urlencoded) 格式的 put 请求 157 | * 158 | * @param url 159 | * @param formParams 160 | * @return 161 | */ 162 | public static Response putForm(String url, Map formParams, Map header) { 163 | return getClient().put(url, MediaTypeEnum.FORM, null, formParams, header); 164 | } 165 | 166 | /** 167 | * delete 请求 168 | * 169 | * @param url 170 | * @param body 171 | * @return 172 | */ 173 | public static Response delete(String url, Object body) { 174 | return getClient().delete(url, MediaTypeEnum.JSON, body, (Map) null, (Map) null); 175 | } 176 | 177 | /** 178 | * delete 请求 179 | * 180 | * @param url 181 | * @param body 182 | * @return 183 | */ 184 | public static Response delete(String url, Object body, Map header) { 185 | return getClient().delete(url, MediaTypeEnum.JSON, body, (Map) null, header); 186 | } 187 | 188 | /** 189 | * form(x-www-form-urlencoded) 格式的 delete 请求 190 | * 191 | * @param url 192 | * @param formParams 193 | * @return 194 | */ 195 | public static Response deleteForm(String url, Map formParams) { 196 | return getClient().delete(url, MediaTypeEnum.FORM, null, formParams, (Map) null); 197 | } 198 | 199 | /** 200 | * form(x-www-form-urlencoded) 格式的 delete 请求 201 | * 202 | * @param url 203 | * @param formParams 204 | * @return 205 | */ 206 | public static Response deleteForm(String url, Map formParams, Map header) { 207 | return getClient().delete(url, MediaTypeEnum.FORM, null, formParams, header); 208 | } 209 | 210 | /** 211 | * form-data(multipart/form-data) 格式的 post 请求 212 | * 213 | * @param url 214 | * @param formParams 215 | * @return 216 | */ 217 | public static Response postFormData(String url, Map formParams) { 218 | return getClient().post(url, MediaTypeEnum.FORM_DATA, null, formParams, (Map) null); 219 | } 220 | 221 | public static Response postFormData(String url, Map formParams, Map header) { 222 | return getClient().post(url, MediaTypeEnum.FORM_DATA, null, formParams, header); 223 | } 224 | 225 | 226 | /** 227 | * form-data(multipart/form-data) 格式的 post 请求 228 | * 229 | * @param url 230 | * @param formParams 231 | * @return 232 | */ 233 | public static Response upload(String url, Map formParams) { 234 | return getClient().post(url, MediaTypeEnum.FORM_DATA, null, formParams, (Map) null); 235 | } 236 | 237 | public static Response upload(String url, Map formParams, Map header) { 238 | return getClient().post(url, MediaTypeEnum.FORM_DATA, null, formParams, header); 239 | } 240 | 241 | public static InputStream down(String url) { 242 | 243 | return getClient().down(url); 244 | } 245 | 246 | 247 | public static void down(String url, String savePath) throws IOException { 248 | InputStream in = down(url); 249 | 250 | File file = new File(savePath); 251 | 252 | boolean isFile = savePath.contains("."); 253 | if (isFile) { 254 | file.getParentFile().mkdirs(); 255 | } else { 256 | file.mkdirs(); 257 | } 258 | 259 | if (!isFile) { 260 | String fName = StringUtils.substringBefore(url, "?"); 261 | fName = StringUtils.substringAfterLast(fName, "/"); 262 | savePath = savePath + (StringUtils.endsWith(savePath, "/") ? "" : "/") + fName; 263 | } 264 | @Cleanup BufferedInputStream bufferedInputStream = new BufferedInputStream(in); 265 | @Cleanup FileOutputStream fileOutputStream = new FileOutputStream(savePath); 266 | byte[] b = new byte[1024]; 267 | while ((bufferedInputStream.read(b)) != -1) { 268 | // 写入数据 269 | fileOutputStream.write(b); 270 | } 271 | } 272 | 273 | //============= 发送http 请求 ================= 274 | 275 | /** 276 | * 同步执行 277 | * 278 | * @param httpRequest 279 | * @return 280 | */ 281 | public static Response send(HttpRequest httpRequest) { 282 | ApiClient apiClient = HttpUtil.getClient(); 283 | Call call = apiClient.buildCall(httpRequest.getUrl(), httpRequest.getMethod(), httpRequest.getMediaType(), 284 | httpRequest.getQueryParams(), httpRequest.getQueryMap(), httpRequest.getBody(), httpRequest.getForm(), 285 | httpRequest.getHeaders()); 286 | return apiClient.execute(call); 287 | } 288 | 289 | 290 | /** 291 | * 异步执行 292 | * 293 | * @param httpRequest 294 | * @param callback 295 | */ 296 | public static void asyncExecute(HttpRequest httpRequest, Callback callback) { 297 | 298 | ApiClient apiClient = HttpUtil.getClient(); 299 | Call call = apiClient.buildCall(httpRequest.getUrl(), httpRequest.getMethod(), httpRequest.getMediaType(), 300 | httpRequest.getQueryParams(), httpRequest.getQueryMap(), httpRequest.getBody(), httpRequest.getForm(), 301 | httpRequest.getHeaders()); 302 | apiClient.executeAsync(call, callback); 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.github.admin4j 7 | common-http 8 | pom 9 | ${revision} 10 | 11 | 12 | admin4j-common-http-starter 13 | admin4j-common-http 14 | 15 | common-http 16 | HTTP request library packaged specifically for JAVA 17 | https://github.com/andanyoung 18 | 19 | UTF-8 20 | 1.8 21 | 0.9.6 22 | 23 | 24 | 25 | 26 | 27 | org.codehaus.mojo 28 | flatten-maven-plugin 29 | 1.4.0 30 | 31 | 32 | pom-xml-flattened 33 | true 34 | resolveCiFriendliesOnly 35 | 36 | 37 | 38 | flatten 39 | process-resources 40 | 41 | flatten 42 | 43 | 44 | 45 | flatten.clean 46 | clean 47 | 48 | clean 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 3.10.1 57 | 58 | utf-8 59 | 8 60 | 8 61 | 62 | 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-source-plugin 67 | 2.2.1 68 | 69 | 70 | attach-sources 71 | 72 | jar-no-fork 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | The Apache Software License, Version 2.0 84 | http://www.apache.org/licenses/LICENSE-2.0.txt 85 | repo 86 | 87 | 88 | 89 | 90 | scm:git@github.com:admin4j/common-http.git 91 | scm:git@github.com:admin4j/common-http.git 92 | 93 | https://github.com/admin4j/common-http 94 | 95 | 96 | 97 | 98 | admin4j 99 | 1218853253@qq.com 100 | https://github.com/andanyoung 101 | +8 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | ossrh 122 | 123 | false 124 | 125 | 126 | 127 | 128 | 129 | org.apache.maven.plugins 130 | maven-source-plugin 131 | 2.2.1 132 | 133 | 134 | attach-sources 135 | 136 | jar-no-fork 137 | 138 | 139 | 140 | 141 | 142 | org.apache.maven.plugins 143 | maven-javadoc-plugin 144 | 2.9.1 145 | 146 | private 147 | true 148 | UTF-8 149 | UTF-8 150 | UTF-8 151 | 152 | -Xdoclint:none 153 | 154 | 155 | 156 | package 157 | 158 | jar 159 | 160 | 161 | 162 | 163 | 164 | org.springframework.boot 165 | spring-boot-maven-plugin 166 | 2.7.12 167 | 168 | 169 | 170 | org.projectlombok 171 | lombok 172 | 173 | 174 | org.springframework.boot 175 | spring-boot-autoconfigure-processor 176 | 177 | 178 | org.springframework.boot 179 | spring-boot-autoconfigure 180 | 181 | 182 | org.springframework.boot 183 | spring-boot-configuration-processor 184 | 185 | 186 | 187 | 188 | 189 | org.sonatype.central 190 | central-publishing-maven-plugin 191 | 0.7.0 192 | true 193 | 194 | ossrh 195 | true 196 | published 197 | true 198 | 199 | 200 | 201 | 202 | org.apache.maven.plugins 203 | maven-gpg-plugin 204 | 3.1.0 205 | 206 | 207 | sign-artifacts 208 | verify 209 | 210 | sign 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OKHttpUtil 2 | 3 | 在Java的世界中,Http客户端之前一直是Apache家的HttpClient占据主导,但是由于此包较为庞大,API又比较难用,因此并不使用很多场景。而新兴的OkHttp、Jodd-http固然好用,但是面对一些场景时,学习成本还是有一些的。很多时候,我们想追求轻量级的Http客户端,并且追求简单易用。而OKHttp 4 | 是一套处理 HTTP 网络请求的依赖库,由 Square 公司设计研发并开源,目前可以在 Java 和 Kotlin 中使用。对于 Android App 5 | 来说,OkHttp 现在几乎已经占据了所有的网络请求操作,对于服务器端请求外部接口也是必备的选择 。针对OKHttp 6 | OkHttpUtil做了一层封装,使Http请求变得无比简单。 7 | 8 | # OKHttpUtil 功能 9 | 10 | - 根据URL自动判断是请求HTTP还是HTTPS,不需要单独写多余的代码。 11 | - 默认情况下Cookie自动记录,比如可以实现模拟登录,即第一次访问登录URL后后续请求就是登录状态。 12 | - 自动识别304跳转并二次请求 13 | - 支持代理配置 14 | - 支持referer配置 15 | - 支持User-Agent配置 16 | - 自动识别并解压Gzip格式返回内容 17 | - 支持springboot 配置文件 18 | - 极简的封装调用 19 | 20 | # OKHttpUtil使用 21 | 22 | maven引入 23 | 24 | ```xml 25 | 26 | 27 | io.github.admin4j 28 | http 29 | 0.9.6 30 | 31 | ``` 32 | 33 | 最新版查询 [https://central.sonatype.com/artifact/io.github.admin4j/http](https://central.sonatype.com/artifact/io.github.admin4j/http) 34 | 35 | ## GET 36 | 37 | 最简单的使用莫过于用HttpUtil工具类快速请求某个接口: 38 | 39 | ``` 40 | Response response = HttpUtil.get("https://github.com/search", Pair.of("q", "okhttp")); 41 | System.out.println("response = " + response); 42 | ``` 43 | 44 | ## POST 45 | 46 | 一行代码即可搞定,当然Post请求也很简单: 47 | 48 | ``` 49 | # JSON 格式的body 50 | Response post = HttpUtil.post("https://oapi.dingtalk.com/robot/send?access_token=27f5954ab60ea8b2e431ae9101b1289c138e85aa6eb6e3940c35ee13ff8b6335", "{\"msgtype\": \"text\",\"text\": {\"content\":\"【反馈提醒】我就是我, 是不一样的烟火\"}}"); 51 | System.out.println("post = " + post); 52 | 53 | # form 请求 54 | Map formParams = new HashMap<>(16); 55 | formParams.put("username", "admin"); 56 | formParams.put("password", "admin123"); 57 | Response response = HttpUtil.postForm("http://192.168.1.13:9100/auth/login", 58 | formParams 59 | ); 60 | System.out.println("response = " + response); 61 | ``` 62 | 63 | 返回格式为JSON的 可以使用 HttpJsonUtil 自动返回JsonObject 64 | 65 | ``` 66 | Map object=HttpJsonUtil.get("https://github.com/search", 67 | Pair.of("q","http"), 68 | Pair.of("username","agonie201218")); 69 | System.out.println("object = "+object); 70 | ``` 71 | 72 | ## 文件上传 73 | 74 | ``` 75 | File file=new File("C:\\Users\\andanyang\\Downloads\\Sql.txt"); 76 | Map formParams=new HashMap<>(); 77 | formParams.put("key","test"); 78 | formParams.put("file",file); 79 | formParams.put("token","WXyUseb-D4sCum-EvTIDYL-mEehwDtrSBg-Zca7t:qgOcR2gUoKmxt-VnsNb657Oatzo=:eyJzY29wZSI6InpoYW56aGkiLCJkZWFkbGluZSI6MTY2NTMwNzUxNH0="); 80 | Response response=HttpUtil.upload("https://upload.qiniup.com/",formParams); 81 | System.out.println(response); 82 | ``` 83 | 84 | ## 下载文件 85 | 86 | ``` 87 | HttpUtil.down("https://gitee.com/admin4j/common-http","path/"); 88 | ``` 89 | 90 | ## HttpRequest 链式请求 91 | 92 | ``` 93 | 94 | # get 95 | Response response=HttpRequest.get("https://search.gitee.com/?skin=rec&type=repository") 96 | .queryMap("q","admin4j") 97 | .header(HttpHeaderKey.USER_AGENT,"admin4j") 98 | .execute(); 99 | System.out.println("response = "+response); 100 | 101 | # post form 102 | Response response=HttpRequest.get("http://192.168.1.13:9100/auth/login") 103 | .queryMap("q","admin4j") 104 | .header(HttpHeaderKey.USER_AGENT,"admin4j") 105 | .form("username","admin") 106 | .form("password","admin123") 107 | .execute(); 108 | System.out.println("response = "+response); 109 | ``` 110 | 111 | ### 开启日志 112 | 113 | ```yaml 114 | logging: 115 | level: 116 | io.github.admin4j.http.core: debug 117 | ``` 118 | 119 | post form 日志 120 | 121 | ``` 122 | 16:49:14.092[main]DEBUG io.github.admin4j.http.core.HttpLogger- -->GET http://192.168.1.13:9100/auth/login?q=admin4j http/1.1 123 | 16:49:14.094[main]DEBUG io.github.admin4j.http.core.HttpLogger-User-Agent:admin4j 124 | 16:49:14.094[main]DEBUG io.github.admin4j.http.core.HttpLogger-Host:192.168.1.13:9100 125 | 16:49:14.094[main]DEBUG io.github.admin4j.http.core.HttpLogger-Connection:Keep-Alive 126 | 16:49:14.094[main]DEBUG io.github.admin4j.http.core.HttpLogger-Accept-Encoding:gzip 127 | 16:49:14.094[main]DEBUG io.github.admin4j.http.core.HttpLogger- -->END GET 128 | 16:49:14.670[main]DEBUG io.github.admin4j.http.core.HttpLogger-<--200OK http://192.168.1.13:9100/auth/login?q=admin4j (575ms) 129 | 16:49:14.670[main]DEBUG io.github.admin4j.http.core.HttpLogger-transfer-encoding:chunked 130 | 16:49:14.670[main]DEBUG io.github.admin4j.http.core.HttpLogger-Vary:Origin 131 | 16:49:14.670[main]DEBUG io.github.admin4j.http.core.HttpLogger-Vary:Access-Control-Request-Method 132 | 16:49:14.670[main]DEBUG io.github.admin4j.http.core.HttpLogger-Vary:Access-Control-Request-Headers 133 | 16:49:14.670[main]DEBUG io.github.admin4j.http.core.HttpLogger-Vary:Origin 134 | 16:49:14.670[main]DEBUG io.github.admin4j.http.core.HttpLogger-Vary:Access-Control-Request-Method 135 | 16:49:14.670[main]DEBUG io.github.admin4j.http.core.HttpLogger-Vary:Access-Control-Request-Headers 136 | 16:49:14.671[main]DEBUG io.github.admin4j.http.core.HttpLogger-Content-Type:application/json;charset=utf-8 137 | 16:49:14.671[main]DEBUG io.github.admin4j.http.core.HttpLogger-Date:Tue,08Nov 2022 08:49:14GMT 138 | 16:49:14.671[main]DEBUG io.github.admin4j.http.core.HttpLogger- 139 | 16:49:14.671[main]DEBUG io.github.admin4j.http.core.HttpLogger-{"code":406,"msg":"Full authentication is required to access this resource"} 140 | 16:49:14.671[main]DEBUG io.github.admin4j.http.core.HttpLogger-<--END HTTP(76-byte body) 141 | response=Response{protocol=http/1.1,code=200,message=OK,url=http://192.168.1.13:9100/auth/login?q=admin4j} 142 | ``` 143 | 144 | # 在 Springboot 中使用 145 | 146 | maven引入 147 | 148 | ```xml 149 | 150 | 151 | io.github.admin4j 152 | common-http-starter 153 | 0.9.6 154 | 155 | ``` 156 | 157 | 最新版查询 [io.github.admin4j:common-http-starter](https://search.maven.org/artifact/io.github.admin4j/common-http-starter) 158 | 159 | spring 版可以对 OkHttp进行个性化配置 160 | 配置详见 161 | 162 | ```java 163 | public class HttpConfig { 164 | 165 | 166 | /** 167 | * 日志等级 168 | */ 169 | private HttpLoggingInterceptor.Level loggLevel = HttpLoggingInterceptor.Level.BODY; 170 | 171 | /** 172 | * 读取超时时间,秒 173 | */ 174 | private long readTimeout = 30; 175 | /** 176 | * 链接超时时间 177 | */ 178 | private long connectTimeout = 30; 179 | 180 | private boolean followRedirects = false; 181 | 182 | /** 183 | * 最大的连接数 184 | */ 185 | private int maxIdleConnections = 5; 186 | 187 | /** 188 | * 最大的kepAlive 时间 秒 189 | */ 190 | private long keepAliveDuration = 5; 191 | 192 | private String userAgent = "OKHTTP"; 193 | /** 194 | * 是否支持cookie 195 | */ 196 | private boolean cookie = false; 197 | private ProxyConfig proxy; 198 | 199 | 200 | @Data 201 | public static class ProxyConfig { 202 | 203 | private Proxy.Type type = Proxy.Type.HTTP; 204 | private String host; 205 | private Integer port = 80; 206 | private String userName; 207 | private String password; 208 | } 209 | } 210 | 211 | ``` 212 | 213 | # 如何快速封装外部接口 214 | 215 | 以实体项目为例,封装 ebay接口 216 | 217 | ```java 218 | public class EbayClient extends ApiJsonClient { 219 | 220 | /** 221 | * 店铺配置 222 | * 223 | * @param storeId 224 | */ 225 | public EbayClient(Long storeId) { 226 | 227 | //TODO 获取店铺相关配置 228 | Map config = new HashMap<>(); 229 | 230 | basePath = "https://api.ebay.com"; 231 | defaultHeaderMap.put("Authorization", "Bearer " + config.get("accessToken")); 232 | defaultHeaderMap.put("X-EBAY-C-MARKETPLACE-ID", config.get("marketplaceId")); 233 | } 234 | } 235 | ``` 236 | 237 | EbayClient 封装ebay api请求 基础类 238 | 239 | ```java 240 | public class EbayInventoryClient extends EbayClient { 241 | 242 | /** 243 | * 店铺配置 244 | * 245 | * @param storeId 246 | */ 247 | public EbayInventoryClient(Long storeId) { 248 | super(storeId); 249 | } 250 | 251 | /** 252 | * 库存列表 253 | * 254 | * @param limit 255 | * @param offset 256 | * @return 257 | * @throws IOException 258 | */ 259 | public Map inventoryItem(Integer limit, Integer offset) throws IOException { 260 | 261 | Map queryMap = new HashMap(2); 262 | queryMap.put("limit", limit); 263 | queryMap.put("offset", offset); 264 | return get("/sell/inventory/v1/inventory_item", queryMap); 265 | } 266 | } 267 | ``` 268 | 269 | EbayInventoryClient 封装ebay 库存 api请求 270 | 使用 271 | 272 | ```java 273 | EbayInventoryClient ebayInventoryClient=new EbayInventoryClient(1L); 274 | Map jsonObject=ebayInventoryClient.inventoryItem(0,10); 275 | ``` 276 | 277 | ```java 278 | /** 279 | * 订单相关api 280 | * 281 | * @author andanyang 282 | * @since 2022/11/8 17:34 283 | */ 284 | public class EbayOrderClient extends EbayClient { 285 | 286 | 287 | /** 288 | * 店铺配置 289 | * 290 | * @param storeId 291 | */ 292 | public EbayOrderClient(Long storeId) { 293 | super(storeId); 294 | } 295 | 296 | /** 297 | * 订单列表 298 | * 299 | * @param beginTime 300 | * @param endTime 301 | * @param limit 302 | * @param offset 303 | * @return 304 | */ 305 | public Map orders(String beginTime, String endTime, int limit, int offset) { 306 | 307 | final String path = "/sell/fulfillment/v1/order"; 308 | 309 | String filter = MessageFormat.format("lastmodifieddate:[{0}..{1}]", beginTime, endTime); 310 | 311 | // 312 | Map queryMap = new HashMap<>(8); 313 | queryMap.put("filter", filter); 314 | queryMap.put("limit", limit); 315 | queryMap.put("offset", offset); 316 | 317 | return get("/sell/inventory/v1/inventory_item", queryMap); 318 | } 319 | } 320 | ``` 321 | 322 | 库存相关的使用`EbayInventoryClient`,订单相关的使用`EbayOrderClient`,是不是很清晰 323 | 324 | # JSON 适配器 325 | 326 | 项目默认使用 fastjson 解析json,如果想用其他json框架.排除`admin4j-json-fastjson`,重新导入其他包参考如下 327 | 328 | ``` 329 | 330 | io.github.admin4j 331 | http 332 | 0.9.6 333 | 334 | 335 | com.admin4j.json 336 | admin4j-json-fastjson 337 | 338 | 339 | 340 | 341 | com.admin4j.json 342 | admin4j-json-jackson 343 | 0.6.1 344 | 345 | ``` 346 | 347 | JSON 适配工具类参考 [https://github.com/admin4j/admin4j-json](https://github.com/admin4j/admin4j-json) 348 | 349 | # Error 报错踩坑 350 | 351 | ``` 352 | 353 | java.lang.NoClassDefFoundError: okio.Buffer 354 | 355 | at okhttp3.ResponseBody$Companion.create(ResponseBody.kt:248) 356 | at okhttp3.ResponseBody$Companion.create$default(ResponseBody.kt:247) 357 | at okhttp3.internal.Util.(Util.kt:65) 358 | at okhttp3.HttpUrl$Builder.parse$okhttp(HttpUrl.kt:1239) 359 | at okhttp3.HttpUrl$Companion.get(HttpUrl.kt:1634) 360 | at okhttp3.Request$Builder.url(Request.kt:184) 361 | at io.github.admin4j.http.core.AbstractHttpBuildCall.buildRequest(AbstractHttpBuildCall.java:182) 362 | at io.github.admin4j.http.core.AbstractHttpBuildCall.buildPost(AbstractHttpBuildCall.java:410) 363 | at io.github.admin4j.http.core.AbstractHttpRequest.post(AbstractHttpRequest.java:59) 364 | ``` 365 | 366 | 由于 okio 版本号问题 367 | 具体参考 [记录Maven 依赖包版本号奇奇怪怪的问题 - okhttp3、okio 版本指定无效](https://blog.csdn.net/agonie201218/article/details/131552134) 368 | 369 | # 鸣谢 370 | ![IntelliJ IDEA logo](https://resources.jetbrains.com/storage/products/company/brand/logos/IntelliJ_IDEA.png) 371 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/ApiJsonClient.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http; 2 | 3 | import com.admin4j.json.JSONUtil; 4 | import com.admin4j.json.mapper.JSONMapper; 5 | import io.github.admin4j.http.core.*; 6 | import io.github.admin4j.http.exception.HttpException; 7 | import io.github.admin4j.http.factory.HttpClientFactory; 8 | import okhttp3.Call; 9 | import okhttp3.Callback; 10 | import okhttp3.Response; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | /** 19 | * @author andanyang 20 | * @since 2022/10/9 16:53 21 | */ 22 | public class ApiJsonClient extends AbstractHttpRequest { 23 | 24 | public ApiJsonClient() { 25 | super(); 26 | } 27 | 28 | public ApiJsonClient(HttpConfig config) { 29 | okHttpClient = HttpClientFactory.okHttpClient(config); 30 | headerMap.put(HttpHeaderKey.USER_AGENT, config.getUserAgent()); 31 | headerMap.put(HttpHeaderKey.REFERER, config.getReferer()); 32 | this.followRedirects = config.isFollowRedirects(); 33 | } 34 | 35 | 36 | @Override 37 | public String serializeJSON(Object obj) { 38 | return JSONUtil.toJSONString(obj); 39 | } 40 | 41 | protected T deserializeJSON(InputStream in, Class tClass, boolean isList) throws IOException { 42 | 43 | if (isList) { 44 | return (T) JSONUtil.parseList(in, tClass); 45 | } 46 | 47 | if (tClass.equals(JSONMapper.class)) { 48 | return (T) JSONUtil.parseMapper(in); 49 | } 50 | 51 | return JSONUtil.parseObject(in, charset, tClass); 52 | } 53 | 54 | /** 55 | * 执行请求,并返回 json反序列化之后的实体数据 56 | * @param call 请求数据 57 | * @param tClass 实体模型 58 | * @return 59 | * @param 60 | * @throws HttpException 61 | */ 62 | public T execute(Call call, Class tClass) throws HttpException { 63 | try { 64 | Response response = call.execute(); 65 | return handleResponse(response, tClass); 66 | } catch (IOException e) { 67 | throw new HttpException(e.getMessage(),e); 68 | } 69 | } 70 | 71 | 72 | public void executeAsync(Call call, final Class tClass, final HttpCallback callback) { 73 | 74 | call.enqueue(new Callback() { 75 | 76 | 77 | @Override 78 | public void onResponse(@NotNull Call call, @NotNull Response response) { 79 | T result; 80 | try { 81 | result = (T) handleResponse(response, tClass); 82 | } catch (Exception e) { 83 | callback.onFailure(e, response.code(), response.headers().toMultimap()); 84 | return; 85 | } 86 | callback.onSuccess(result, response.code(), response.headers().toMultimap()); 87 | } 88 | 89 | @Override 90 | public void onFailure(@NotNull Call call, @NotNull IOException e) { 91 | callback.onFailure(e, 0, null); 92 | } 93 | }); 94 | } 95 | 96 | 97 | // ========== handleResponse ============= 98 | 99 | /** 100 | * Handle the given response, return the deserialized object when the response is successful. 101 | * 102 | * @param Type 103 | * @param response Response 104 | * @param tClass Return type 105 | * @return Type 106 | * @throws HttpException If the response has a unsuccessful status code or 107 | * fail to deserialize the response body 108 | */ 109 | 110 | protected T handleResponse(Response response, Class tClass) throws HttpException { 111 | return handleResponse(response, tClass, false); 112 | } 113 | 114 | protected T handleResponse(Response response, Class tClass, boolean isList) throws HttpException { 115 | 116 | response = handleResponse(response); 117 | 118 | if (response.isSuccessful()) { 119 | try { 120 | if (tClass == null) { 121 | // returning null if the returnType is not defined, 122 | // or the status code is 204 (No Content) 123 | 124 | if (response.body() != null || response.code() == 204) { 125 | response.body().close(); 126 | } 127 | return null; 128 | } else { 129 | return handleSuccessResponse(response, tClass, isList); 130 | } 131 | } catch (IOException e) { 132 | throw new HttpException(e.getMessage(), e, response.code(), response.headers().toMultimap()); 133 | } 134 | } else { 135 | 136 | T o = handleFailResponse(response, tClass); 137 | if (o != null) { 138 | return o; 139 | } 140 | String respBody = null; 141 | if (response.body() != null) { 142 | try { 143 | respBody = response.body().string(); 144 | 145 | } catch (IOException e) { 146 | throw new HttpException(e.getMessage(), e, response.code(), response.headers().toMultimap()); 147 | } 148 | } 149 | throw new HttpException(respBody, response.code(), response.headers().toMultimap(), respBody); 150 | } 151 | } 152 | 153 | /** 154 | * 服务器返回成功,解析成JSON 155 | * 156 | * @param response 157 | * @param tClass 158 | * @param 159 | * @return 160 | * @throws IOException 161 | */ 162 | protected T handleSuccessResponse(Response response, Class tClass, boolean isList) throws IOException { 163 | return deserializeJSON(response.body().byteStream(), tClass, isList); 164 | } 165 | 166 | /** 167 | * 服务器返回S失败,解析成失败的,或者抛出错误 168 | * 169 | * @param response 170 | * @param tClass 171 | * @param 172 | * @return 173 | * @throws IOException 174 | */ 175 | protected T handleFailResponse(Response response, Class tClass) { 176 | return null; 177 | } 178 | 179 | // protected JSONObject serialize(Response response) { 180 | // ResponseBody body = response.body(); 181 | // if (body == null) { 182 | // throw new HttpException("response body is null"); 183 | // } 184 | // try { 185 | // return JSONObject.parseObject(body.string()); 186 | // } catch (IOException e) { 187 | // 188 | // throw new HttpException(e); 189 | // } 190 | //} 191 | 192 | // protected JSONArray serializeList(Response response) { 193 | // ResponseBody body = response.body(); 194 | // if (body == null) { 195 | // throw new HttpException("response body is null"); 196 | // } 197 | // try { 198 | // return JSON.parseArray(body.string()); 199 | // } catch (IOException e) { 200 | // 201 | // throw new HttpException(e); 202 | // } 203 | //} 204 | 205 | //=============== request =============== 206 | public T get(String path, Class tClass, Pair... queryParams) { 207 | Call call = buildGet(path, null, queryParams); 208 | return execute(call, tClass); 209 | } 210 | 211 | public T get(String path, Map queryMap, Class tClass) { 212 | Call call = buildGet(path, queryMap, (Pair) null); 213 | return execute(call, tClass); 214 | } 215 | 216 | public T get(String path, Map queryMap,Map headerMap, Class tClass) { 217 | Call call = buildGet(path, queryMap,headerMap, (Pair) null); 218 | return execute(call, tClass); 219 | } 220 | 221 | 222 | public JSONMapper get(String path, Pair... queryParams) { 223 | Response response = get(path, (Map) null,null, queryParams); 224 | return handleResponse(response, JSONMapper.class); 225 | } 226 | 227 | public JSONMapper get(String path, Map queryMap) { 228 | Response response = get(path, queryMap, (Pair[]) null); 229 | return handleResponse(response, JSONMapper.class); 230 | } 231 | 232 | public JSONMapper get(String path, Map queryMap, Map headerMap) { 233 | Response response = get(path, queryMap, headerMap, (Pair[]) null); 234 | return handleResponse(response, JSONMapper.class); 235 | } 236 | 237 | public List getList(String path, Class tClass, Pair... queryParams) { 238 | Response response = get(path, (Map) null, queryParams); 239 | 240 | return (List) handleResponse(response, tClass, true); 241 | } 242 | 243 | public List getList(String path, Map queryMap, Class tClass) { 244 | Response response = get(path, queryMap, (Pair[]) null); 245 | return (List) handleResponse(response, tClass, true); 246 | } 247 | 248 | public List getList(String path, Map queryMap, Map headerMap, Class tClass) { 249 | Response response = get(path, queryMap, headerMap, (Pair[]) null); 250 | return (List) handleResponse(response, tClass, true); 251 | } 252 | 253 | public T postForm(String url, Map formParams, Class tClass) { 254 | 255 | Response response = post(url, MediaTypeEnum.FORM, null, formParams, null); 256 | return handleResponse(response, tClass); 257 | } 258 | 259 | public T postForm(String url, Map formParams, Map headerParams, Class tClass) { 260 | 261 | Response response = post(url, MediaTypeEnum.FORM, null, formParams, headerParams); 262 | return handleResponse(response, tClass); 263 | } 264 | 265 | public T postFormData(String url, Map formParams, Class tClass) { 266 | 267 | Response response = post(url, MediaTypeEnum.FORM_DATA, null, formParams, null); 268 | return handleResponse(response, tClass); 269 | } 270 | 271 | public T postFormData(String url, Map formParams, Map headerParams, Class tClass) { 272 | 273 | Response response = post(url, MediaTypeEnum.FORM_DATA, null, formParams, headerParams); 274 | return handleResponse(response, tClass); 275 | } 276 | 277 | public T post(String url, Object body, Class tClass) { 278 | 279 | Response response = post(url, MediaTypeEnum.JSON, body, null, null); 280 | return handleResponse(response, tClass); 281 | } 282 | 283 | public T post(String url, Object body, Map headerParams, Class tClass) { 284 | 285 | Response response = post(url, MediaTypeEnum.JSON, body, null, headerParams); 286 | return handleResponse(response, tClass); 287 | } 288 | 289 | 290 | public JSONMapper postForm(String url, Map formParams) { 291 | 292 | Response response = post(url, MediaTypeEnum.FORM, null, formParams, null); 293 | return handleResponse(response, JSONMapper.class); 294 | } 295 | 296 | public JSONMapper postForm(String url, Map formParams, Map headerParams) { 297 | 298 | Response response = post(url, MediaTypeEnum.FORM, null, formParams, headerParams); 299 | return handleResponse(response, JSONMapper.class); 300 | } 301 | 302 | public JSONMapper postFormData(String url, Map formParams) { 303 | 304 | Response response = post(url, MediaTypeEnum.FORM_DATA, null, formParams, null); 305 | return handleResponse(response, JSONMapper.class); 306 | } 307 | 308 | public JSONMapper postFormData(String url, Map formParams, Map headerParams) { 309 | 310 | Response response = post(url, MediaTypeEnum.FORM_DATA, null, formParams, headerParams); 311 | return handleResponse(response, JSONMapper.class); 312 | } 313 | 314 | public JSONMapper post(String url, Object body) { 315 | 316 | Response response = post(url, MediaTypeEnum.JSON, body, null, null); 317 | return handleResponse(response, JSONMapper.class); 318 | } 319 | 320 | public JSONMapper post(String url, Object body, Map headerParams) { 321 | 322 | Response response = post(url, MediaTypeEnum.JSON, body, null, headerParams); 323 | return handleResponse(response, JSONMapper.class); 324 | } 325 | 326 | public JSONMapper put(String url, Object body) { 327 | 328 | Response response = put(url, MediaTypeEnum.JSON, body, null, null); 329 | return handleResponse(response, JSONMapper.class); 330 | } 331 | 332 | public JSONMapper put(String url, Object body, Map headerParams) { 333 | 334 | Response response = put(url, MediaTypeEnum.JSON, body, null, headerParams); 335 | return handleResponse(response, JSONMapper.class); 336 | } 337 | 338 | public T put(String url, Object body, Class tClass) { 339 | 340 | Response response = put(url, MediaTypeEnum.JSON, body, null, null); 341 | return handleResponse(response, tClass); 342 | } 343 | 344 | public T put(String url, Object body, Map headerParams, Class tClass) { 345 | 346 | Response response = put(url, MediaTypeEnum.JSON, body, null, headerParams); 347 | return handleResponse(response, tClass); 348 | } 349 | 350 | public JSONMapper putForm(String url, Map formParams) { 351 | 352 | Response response = put(url, MediaTypeEnum.FORM, null, formParams, null); 353 | return handleResponse(response, JSONMapper.class); 354 | } 355 | 356 | public JSONMapper putForm(String url, Map formParams, Map headerParams) { 357 | 358 | Response response = put(url, MediaTypeEnum.FORM, null, formParams, headerParams); 359 | return handleResponse(response, JSONMapper.class); 360 | } 361 | 362 | public T putForm(String url, Map formParams, Class tClass) { 363 | 364 | Response response = put(url, MediaTypeEnum.FORM, null, formParams, null); 365 | return handleResponse(response, tClass); 366 | } 367 | 368 | public T putForm(String url, Map formParams, Map headerParams, Class tClass) { 369 | 370 | Response response = put(url, MediaTypeEnum.FORM, null, formParams, headerParams); 371 | return handleResponse(response, tClass); 372 | } 373 | 374 | public JSONMapper putFormData(String url, Map formParams) { 375 | 376 | Response response = put(url, MediaTypeEnum.FORM_DATA, null, formParams, null); 377 | return handleResponse(response, JSONMapper.class); 378 | } 379 | 380 | public JSONMapper putFormData(String url, Map formParams, Map headerParams) { 381 | 382 | Response response = put(url, MediaTypeEnum.FORM_DATA, null, formParams, headerParams); 383 | return handleResponse(response, JSONMapper.class); 384 | } 385 | 386 | public T delete(String url, 387 | Object body, 388 | Map formParams, 389 | Class tClass) { 390 | 391 | Response response = delete(url, body == null ? MediaTypeEnum.FORM : MediaTypeEnum.JSON, body, formParams, (Map) null); 392 | return handleResponse(response, tClass); 393 | } 394 | 395 | public T delete(String url, 396 | Object body, 397 | Map formParams, 398 | Map headerParams, 399 | Class tClass) { 400 | 401 | Response response = delete(url, body == null ? MediaTypeEnum.FORM : MediaTypeEnum.JSON, body, formParams, headerParams); 402 | return handleResponse(response, tClass); 403 | } 404 | 405 | public JSONMapper delete(String url, 406 | Object body, 407 | Map formParams) { 408 | 409 | Response response = delete(url, body == null ? MediaTypeEnum.FORM : MediaTypeEnum.JSON, body, formParams, (Map) null); 410 | return handleResponse(response, JSONMapper.class); 411 | } 412 | 413 | public JSONMapper delete(String url, 414 | Object body, 415 | Map formParams 416 | , Map headerParams) { 417 | 418 | Response response = delete(url, body == null ? MediaTypeEnum.FORM : MediaTypeEnum.JSON, body, formParams, headerParams); 419 | return handleResponse(response, JSONMapper.class); 420 | } 421 | } 422 | -------------------------------------------------------------------------------- /admin4j-common-http/src/main/java/io/github/admin4j/http/core/AbstractHttpBuildCall.java: -------------------------------------------------------------------------------- 1 | package io.github.admin4j.http.core; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import okhttp3.*; 5 | import okhttp3.internal.http.HttpMethod; 6 | import org.apache.commons.lang3.ObjectUtils; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | import java.io.File; 10 | import java.io.UnsupportedEncodingException; 11 | import java.net.URLConnection; 12 | import java.net.URLEncoder; 13 | import java.nio.charset.StandardCharsets; 14 | import java.time.LocalDate; 15 | import java.time.OffsetDateTime; 16 | import java.util.Collection; 17 | import java.util.Date; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | /** 22 | * @author andanyang 23 | * @since 2022/4/21 13:28 24 | */ 25 | @Slf4j 26 | public abstract class AbstractHttpBuildCall { 27 | 28 | /** 29 | * 请求头 30 | */ 31 | protected Map headerMap = new HashMap<>(); 32 | /** 33 | * 基础URL 34 | */ 35 | protected String baseUrl = null; 36 | 37 | /** 38 | * 字符编码格式 39 | */ 40 | protected java.nio.charset.Charset charset = StandardCharsets.UTF_8; 41 | 42 | /** 43 | * 从子类中获取 初始化好的 OkHttpClient 44 | * 45 | * @return 46 | */ 47 | public abstract OkHttpClient getHttpClient(); 48 | 49 | /** 50 | * 默认的 MediaTypeEnum 51 | * 52 | * @return 53 | */ 54 | protected MediaType getMediaType() { 55 | if (headerMap.containsKey("Content-Type")) { 56 | return MediaType.parse(headerMap.get("Content-Type")); 57 | } 58 | return null; 59 | } 60 | 61 | /** 62 | * Escape the given string to be used as URL query value. 63 | * 64 | * @param str String to be escaped 65 | * @return Escaped string 66 | */ 67 | public String escapeString(String str) { 68 | try { 69 | return URLEncoder.encode(str, "utf8").replace("\\+", "%20"); 70 | } catch (UnsupportedEncodingException e) { 71 | return str; 72 | } 73 | } 74 | 75 | /** 76 | * Build full URL by concatenating base path, the given sub path and query parameters. 77 | * 78 | * @param path The sub path 79 | * @param queryParams The query parameters 80 | * @param queryMap The query map 81 | * @return The full URL 82 | */ 83 | public String buildUrl(String path, Pair[] queryParams, Map queryMap) { 84 | if (StringUtils.isBlank(baseUrl) && queryParams == null && queryMap == null) { 85 | return path; 86 | } 87 | final StringBuilder url = new StringBuilder(); 88 | if (StringUtils.startsWithIgnoreCase(path, "http")) { 89 | url.append(path); 90 | } else { 91 | url.append(baseUrl).append(path); 92 | } 93 | 94 | if (queryParams != null && queryParams.length > 0) { 95 | // support (constant) query string in `path`, e.g. "/posts?draft=1" 96 | String prefix = path.contains("?") ? "&" : "?"; 97 | for (Pair param : queryParams) { 98 | if (param.getValue() != null) { 99 | if (prefix != null) { 100 | url.append(prefix); 101 | prefix = null; 102 | } else { 103 | url.append("&"); 104 | } 105 | String value = parameterToString(param.getValue()); 106 | url.append(escapeString(param.getName())).append("=").append(escapeString(value)); 107 | } 108 | } 109 | } 110 | 111 | if (queryMap != null && !queryMap.isEmpty()) { 112 | // support (constant) query string in `path`, e.g. "/posts?draft=1" 113 | String prefix = url.toString().contains("?") ? "&" : "?"; 114 | for (Map.Entry param : queryMap.entrySet()) { 115 | if (param.getValue() != null) { 116 | if (prefix != null) { 117 | url.append(prefix); 118 | prefix = null; 119 | } else { 120 | url.append("&"); 121 | } 122 | String value = parameterToString(param.getValue()); 123 | url.append(escapeString(param.getKey())).append("=").append(escapeString(value)); 124 | } 125 | } 126 | } 127 | 128 | return url.toString(); 129 | } 130 | 131 | public String buildUrl(String path, Pair[] queryParams) { 132 | return buildUrl(path, queryParams, null); 133 | } 134 | 135 | public String buildUrl(String path, Map queryMap) { 136 | return buildUrl(path, null, queryMap); 137 | } 138 | 139 | /** 140 | * Set header parameters to the request builder, including default headers. 141 | * 142 | * @param headerParams Header parameters in the ofrm of Map 143 | * @param reqBuilder Reqeust.Builder 144 | */ 145 | protected void processHeaderParams(Map headerParams, Request.Builder reqBuilder) { 146 | for (Map.Entry header : headerMap.entrySet()) { 147 | if (headerParams == null || !headerParams.containsKey(header.getKey())) { 148 | reqBuilder.header(header.getKey(), parameterToString(header.getValue())); 149 | } 150 | } 151 | 152 | if (ObjectUtils.isEmpty(headerParams)) { 153 | return; 154 | } 155 | for (Map.Entry param : headerParams.entrySet()) { 156 | reqBuilder.header(param.getKey(), parameterToString(param.getValue())); 157 | } 158 | } 159 | 160 | /** 161 | * Build an HTTP request with the given options. 162 | * 163 | * @param url The sub-path of the HTTP URL 164 | * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" 165 | * @param body The request body object 166 | * @param headerParams The header parameters 167 | * @param formParams The form parameters 168 | * @return The HTTP request 169 | */ 170 | protected Request buildRequest(String url, 171 | Method method, 172 | MediaTypeEnum mediaType, 173 | Object body, 174 | Map formParams, 175 | Map headerParams) { 176 | 177 | //handleBeforeBuildRequest() 178 | if (!StringUtils.startsWithIgnoreCase(url, "http")) { 179 | url = buildUrl(url, null, null); 180 | } 181 | 182 | final Request.Builder reqBuilder = new Request.Builder().url(url); 183 | processHeaderParams(headerParams, reqBuilder); 184 | 185 | RequestBody reqBody; 186 | //GET or HEAD 187 | if (!HttpMethod.permitsRequestBody(method.name())) { 188 | return reqBuilder 189 | .method(method.name(), null) 190 | .build(); 191 | } 192 | 193 | if (mediaType == null) { 194 | Object contentType = ObjectUtils.isEmpty(headerParams) ? null : headerParams.get("Content-Type"); 195 | // ensuring a default content type 196 | mediaType = contentType == null ? MediaTypeEnum.JSON : MediaTypeEnum.of(contentType); 197 | } 198 | 199 | 200 | if (MediaTypeEnum.FORM.equals(mediaType)) { 201 | reqBody = buildRequestBodyFormEncoding(formParams); 202 | } else if (MediaTypeEnum.FORM_DATA.equals(mediaType)) { 203 | reqBody = buildRequestBodyMultipart(formParams); 204 | } else { 205 | MediaType media = getMediaType(); 206 | media = media == null ? mediaType.getMediaType() : media; 207 | if (body == null) { 208 | if (Method.DELETE.equals(method)) { 209 | // allow calling DELETE without sending a request body 210 | reqBody = null; 211 | } else { 212 | // use an empty request body (for POST, PUT and PATCH) 213 | 214 | reqBody = RequestBody.create(media, ""); 215 | } 216 | } else { 217 | reqBody = serialize(body, media); 218 | } 219 | } 220 | return reqBuilder.method(method.name(), reqBody).build(); 221 | } 222 | 223 | /** 224 | * Serialize the given Java object into request body according to the object's 225 | * class and the request Content-Type. 226 | * 227 | * @param obj The Java object 228 | * @return The serialized request body 229 | */ 230 | protected RequestBody serialize(Object obj, MediaType mediaType) { 231 | if (obj instanceof byte[]) { 232 | // Binary (byte array) body parameter support. 233 | return RequestBody.create(mediaType, (byte[]) obj); 234 | } else if (obj instanceof File) { 235 | // File body parameter support. 236 | return RequestBody.create(mediaType, (File) obj); 237 | } else if (obj instanceof String) { 238 | return RequestBody.create(mediaType, (String) obj); 239 | } else { 240 | String content; 241 | if (obj != null) { 242 | content = serializeJSON(obj); 243 | } else { 244 | content = ""; 245 | } 246 | 247 | 248 | byte[] bytes = content.getBytes(charset); 249 | return RequestBody.create(mediaType, bytes); 250 | //return RequestBody.create(content, mediaType); 251 | } 252 | } 253 | 254 | public abstract String serializeJSON(Object obj); 255 | 256 | protected String parameterToString(Object param) { 257 | if (param == null) { 258 | return ""; 259 | } else if (param instanceof Date || param instanceof OffsetDateTime || param instanceof LocalDate) { 260 | //Serialize to json string and remove the " enclosing characters 261 | String jsonStr = serializeJSON(param); 262 | return jsonStr.substring(1, jsonStr.length() - 1); 263 | } else if (param instanceof Collection) { 264 | StringBuilder b = new StringBuilder(); 265 | for (Object o : (Collection) param) { 266 | if (b.length() > 0) { 267 | b.append(","); 268 | } 269 | b.append(o); 270 | } 271 | return b.toString(); 272 | } else if (param instanceof String) { 273 | return (String) param; 274 | } else { 275 | return String.valueOf(param); 276 | } 277 | } 278 | 279 | /** 280 | * Build a form-encoding request body with the given form parameters. 281 | * 282 | * @param formParams Form parameters in the form of Map 283 | * @return RequestBody 284 | */ 285 | protected RequestBody buildRequestBodyFormEncoding(Map formParams) { 286 | 287 | if (formParams == null || formParams.isEmpty()) { 288 | FormBody.Builder builder = new FormBody.Builder(); 289 | return builder.build(); 290 | } 291 | FormBody.Builder builder = new FormBody.Builder(); 292 | for (Map.Entry param : formParams.entrySet()) { 293 | builder.add(param.getKey(), parameterToString(param.getValue())); 294 | } 295 | return builder.build(); 296 | } 297 | 298 | /** 299 | * Build a multipart (file uploading) request body with the given form parameters, 300 | * which could contain text fields and file fields. 301 | * 302 | * @param formParams Form parameters in the form of Map 303 | * @return RequestBody 304 | */ 305 | protected RequestBody buildRequestBodyMultipart(Map formParams) { 306 | 307 | MultipartBody.Builder builder = new MultipartBody.Builder().setType(MediaTypeEnum.FORM_DATA.getMediaType()); 308 | 309 | for (Map.Entry param : formParams.entrySet()) { 310 | if (param.getValue() instanceof File) { 311 | File file = (File) param.getValue(); 312 | Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\"; filename=\"" + file.getName() + "\""); 313 | MediaType mediaType = guessContentTypeFromFile(file); 314 | builder.addPart(partHeaders, RequestBody.create(mediaType, file)); 315 | } else { 316 | Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\""); 317 | builder.addPart(partHeaders, RequestBody.create(null, parameterToString(param.getValue()))); 318 | } 319 | } 320 | return builder.build(); 321 | } 322 | 323 | protected MediaType guessContentTypeFromFile(File file) { 324 | String contentType = URLConnection.guessContentTypeFromName(file.getName()); 325 | if (contentType == null) { 326 | return MediaTypeEnum.OCTET_STREAM.getMediaType(); 327 | } else { 328 | return MediaType.parse(contentType); 329 | } 330 | } 331 | 332 | /** 333 | * Build HTTP call with the given options. 334 | * 335 | * @param path The sub-path of the HTTP URL 336 | * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" 337 | * @param queryParams The query parameters 338 | * @param body The request body object 339 | * @param headerParams The header parameters 340 | * @param formParams The form parameters 341 | * @return The HTTP call 342 | */ 343 | public Call buildCall(String path, 344 | Method method, 345 | MediaTypeEnum mediaType, 346 | Pair[] queryParams, 347 | Map queryMap, 348 | Object body, 349 | Map formParams, 350 | Map headerParams 351 | ) { 352 | 353 | final String url = buildUrl(path, queryParams, queryMap); 354 | Request request = buildRequest(url, method, mediaType, body, formParams, headerParams); 355 | return getHttpClient().newCall(request); 356 | } 357 | 358 | /** 359 | * Build HTTP call with the given options. 360 | * 361 | * @param url The sub-path of the HTTP URL 362 | * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" 363 | * @param body The request body object 364 | * @param formParams The query parameters 365 | * @param headerParams The header parameters 366 | * @param formParams The form parameters 367 | * @return The HTTP call 368 | */ 369 | public Call buildCall(String url, 370 | Method method, 371 | Object body, 372 | Map formParams, 373 | Map headerParams) { 374 | return buildCall(url, method, null, null, null, body, formParams, headerParams); 375 | } 376 | 377 | /** 378 | * 重定向请求 379 | * 380 | * @param response 381 | * @return 382 | */ 383 | protected Request buildRedirectRequest(Response response) { 384 | 385 | String location = response.header("Location"); 386 | if (StringUtils.isBlank(location)) { 387 | return null; 388 | } 389 | 390 | Request request = response.request(); 391 | HttpUrl url = request.url().resolve(location); 392 | 393 | Request.Builder builder = request.newBuilder(); 394 | return builder.url(url).build(); 395 | } 396 | 397 | public Call buildGet(String path, Map queryMap, Pair... queryParams) { 398 | 399 | return buildCall(path, Method.GET, null, queryParams, queryMap, null, null, null); 400 | } 401 | 402 | public Call buildGet(String path, Map queryMap,Map headerMap, Pair... queryParams) { 403 | 404 | return buildCall(path, Method.GET, null, queryParams, queryMap, null, null, headerMap); 405 | } 406 | 407 | // ======================= build GET POST =============== 408 | 409 | protected Call buildPost(String url, 410 | MediaTypeEnum mediaTypeEnum, 411 | Object body, 412 | Map formParams, 413 | Map headerParams) { 414 | 415 | Request request = buildRequest(url, Method.POST, mediaTypeEnum, body, formParams, headerParams); 416 | 417 | return getHttpClient().newCall(request); 418 | } 419 | 420 | protected Call buildPut(String url, 421 | MediaTypeEnum mediaTypeEnum, 422 | Object body, 423 | Map formParams, 424 | Map headerParams) { 425 | 426 | Request request = buildRequest(url, Method.PUT, mediaTypeEnum, body, formParams, headerParams); 427 | 428 | return getHttpClient().newCall(request); 429 | } 430 | 431 | protected Call buildDelete(String url, 432 | MediaTypeEnum mediaTypeEnum, 433 | Object body, 434 | Map formParams, 435 | Map headerParams) { 436 | 437 | Request request = buildRequest(url, Method.DELETE, mediaTypeEnum, body, formParams, headerParams); 438 | 439 | return getHttpClient().newCall(request); 440 | } 441 | } 442 | --------------------------------------------------------------------------------