一、前言
最近再寫調(diào)用三方接口傳輸數(shù)據(jù)的項(xiàng)目,這篇博客記錄項(xiàng)目完成的過程,方便后續(xù)再碰到類似的項(xiàng)目可以快速上手
項(xiàng)目結(jié)構(gòu):
二、編碼
這里主要介紹HttpClient發(fā)送POST請求工具類和定時(shí)器的使用,mvc三層架構(gòu)編碼不做探究
pom.xml
<dependencies>
<!--web啟動依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<!--swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--測試單元-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application-dev.yml
#####端口配置#####
server:
port: 9991
#####數(shù)據(jù)源配置#####
spring:
datasource:
username: dev
password: dev1234
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#druid 數(shù)據(jù)源專有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置監(jiān)控統(tǒng)計(jì)攔截的filters,stat:監(jiān)控統(tǒng)計(jì)、log4j:日志記錄、wall:防御sql注入
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
#####mybatis配置#####
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.jzj.pojo
configuration:
map-underscore-to-camel-case: true
#####配置日志文件#####
logging:
config: classpath:logback.xml
#設(shè)置日志級別的節(jié)點(diǎn)
level:
com:
jzj: debug
Constast
package com.jzj.common;
public class Constast {
/**
* 請求頭信息
*/
public static final String CONTENT_TYPE = "application/json;charset=UTF-8";
/**
* 返回狀態(tài)值
*/
public static final Integer OK = 200;
public static final String YK_URL = "三方接口地址";
}
utils
package com.jzj.utils;
import com.jzj.common.Constast;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* Apache HttpClient發(fā)送POST請求工具類
*
* @author 黎明
* @version 1.0
* @date 2023/8/14 14:18
*/
public class HttpUtils {
/**
* 發(fā)送post請求
*
* @param url 請求url
* @param jsonParam 請求參數(shù)
* @return 響應(yīng)數(shù)據(jù)
*/
public static String doPostJson(String url, String jsonParam) {
// 創(chuàng)建一個(gè)HttpPost對象,并指定URL
HttpPost httpPost = new HttpPost(url);
// 聲明一個(gè)CloseableHttpResponse對象來接收請求的響應(yīng)
CloseableHttpResponse response = null;
// 創(chuàng)建一個(gè)CloseableHttpClient對象。wrapClient方法是自定義的方法,用于構(gòu)建和配置HttpClient對象
CloseableHttpClient httpClient = wrapClient(url);
try {
// 通過重新賦值的方式為HttpPost對象設(shè)置URL
httpPost = new HttpPost(url);
// 設(shè)置請求頭的內(nèi)容類型。Constast.CONTENT_TYPE表示請求的數(shù)據(jù)類型
httpPost.setHeader("Content-type", Constast.CONTENT_TYPE);
// 創(chuàng)建一個(gè)StringEntity對象,用于封裝JSON參數(shù)。
StringEntity entity = new StringEntity(jsonParam, "UTF-8");
// 將實(shí)體的內(nèi)容編碼設(shè)置為與請求頭的內(nèi)容類型相同
entity.setContentEncoding(new BasicHeader("Content-type", Constast.CONTENT_TYPE));
// 將StringEntity對象設(shè)置為HttpPost請求的實(shí)體
httpPost.setEntity(entity);
// 執(zhí)行HttpPost請求,并將響應(yīng)賦值給response對象
response = httpClient.execute(httpPost);
// 判斷響應(yīng)的狀態(tài)碼是否等于200
if (response.getStatusLine().getStatusCode() == Constast.OK) {
// 將響應(yīng)實(shí)體轉(zhuǎn)換為字符串并返回。EntityUtils.toString方法用于讀取響應(yīng)實(shí)體的內(nèi)容。
return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
}
} catch (Exception e) {
throw new RuntimeException("[發(fā)送POST請求錯(cuò)誤:]" + e.getMessage());
} finally {
// 釋放連接、關(guān)閉響應(yīng)和關(guān)閉HttpClient對象
try {
httpPost.releaseConnection();
response.close();
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 根據(jù)URL的協(xié)議來配置HttpClient對象
* @param url url地址
* @return CloseableHttpClient
*/
private static CloseableHttpClient wrapClient(String url) {
// 使用HttpClientBuilder類創(chuàng)建一個(gè)默認(rèn)的HttpClient對象
CloseableHttpClient client = HttpClientBuilder.create().build();
if (url.startsWith("https")) { // 檢查URL是否以"https"開頭,以確定是否需要使用HTTPS協(xié)議
// 如果URL以"https"開頭,調(diào)用方法獲取配置了HTTPS支持的CloseableHttpClient對象
client = getCloseableHttpsClients();
}
return client;
}
/**
* 創(chuàng)建一個(gè)支持HTTPS的CloseableHttpClient對象
* @return CloseableHttpClient
*/
private static CloseableHttpClient getCloseableHttpsClients() {
// 采用繞過驗(yàn)證的方式處理https請求
SSLClient ssl = new SSLClient();
SSLContext sslcontext = ssl.createIgnoreVerifySSL();
// 設(shè)置協(xié)議http和https對應(yīng)的處理socket鏈接工廠的對象
org.apache.http.config.Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new SSLConnectionSocketFactory(sslcontext)).build();
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
HttpClients.custom().setConnectionManager(connManager);
// 創(chuàng)建自定義的httpsclient對象
CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).build();
return client;
}
}
package com.jzj.utils;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
/**
* 用于創(chuàng)建一個(gè)支持繞過HTTPS驗(yàn)證的SSLContext對象
*
* @author 黎明
* @version 1.0
* @date 2023/8/14 14:35
*/
public class SSLClient {
// 使用@SuppressWarnings注解來抑制未使用的警告
@SuppressWarnings("unused")
public SSLContext createIgnoreVerifySSL() {
// 創(chuàng)建套接字對象
SSLContext sslContext = null;
try {
// 指定TLS版本
sslContext = SSLContext.getInstance("TLSv1.2");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("[創(chuàng)建套接字失敗:] " + e.getMessage());
}
// 實(shí)現(xiàn)X509TrustManager接口,用于繞過驗(yàn)證
X509TrustManager trustManager = new X509TrustManager() {
// 該方法用于驗(yàn)證客戶端證書
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException {
}
// 該方法用于驗(yàn)證服務(wù)器證書
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException {
}
// 該方法返回受信任的頒發(fā)機(jī)構(gòu)(證書頒發(fā)機(jī)構(gòu))數(shù)組。在這里,返回null表示不對頒發(fā)機(jī)構(gòu)進(jìn)行限制
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
};
try {
// 初始化sslContext對象
sslContext.init(null, new TrustManager[]{trustManager}, null);
} catch (KeyManagementException e) {
throw new RuntimeException("[初始化套接字失敗:] " + e.getMessage());
}
return sslContext;
}
}
scheduled
package com.jzj.scheduled;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jzj.common.Constast;
import com.jzj.pojo.TrsfToYk;
import com.jzj.pojo.TrsfToYkLog;
import com.jzj.service.SfSfmxService;
import com.jzj.service.TrsfToYkLogService;
import com.jzj.service.TrsfToYkService;
import com.jzj.utils.HttpUtils;
import com.jzj.vo.YkResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 定時(shí)器任務(wù)
*
* @author 黎明
* @version 1.0
* @date 2023/8/15 15:03
*/
@Component
@Slf4j
public class TrsfToYkScheduled {
// 注入trsfToYkService
@Autowired
private TrsfToYkService trsfToYkService;
// 注入trsfToYkLogService
@Autowired
private TrsfToYkLogService trsfToYkLogService;
// 注入SfSfmxService
@Autowired
private SfSfmxService sfSfmxService;
/**
* 審方信息下傳英克
*/
@Scheduled(cron = "*/10 * * * * *")
public void toYk() {
// 根據(jù)視圖查詢所有bs=0審方信息
List<TrsfToYk> sfAllInfo = trsfToYkService.findAll();
if (sfAllInfo.size() != 0) { // 判斷是否有數(shù)據(jù)
ObjectMapper mapper = new ObjectMapper();
String requestData = null;
try {
requestData = mapper.writeValueAsString(sfAllInfo);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
log.info("發(fā)送的數(shù)據(jù)是:{}", requestData);
String responseData = HttpUtils.doPostJson(Constast.YK_URL, requestData);
log.info("響應(yīng)的數(shù)據(jù)是:{}", responseData);
JSONObject responseJson = JSONObject.parseObject(responseData);
YkResultVo ykResultVo = responseJson.toJavaObject(YkResultVo.class);
// 判斷響應(yīng)狀態(tài)是否為200
if (ykResultVo.getStatus().equals("200") && ykResultVo.getStatus() != null) {
// 記錄日志
ykResultVo.getData().stream().forEach(v -> {
TrsfToYkLog trsfToYkLog = new TrsfToYkLog();
trsfToYkLog.setAuditId(v.getAuditId());
trsfToYkLog.setStatus(v.getStatus());
trsfToYkLogService.insertLog(trsfToYkLog);
});
// 更新審方明細(xì)表bs字段
ykResultVo.getData().stream().filter(v -> v.getStatus().equals("200")).forEach(v -> {
long aid = Long.parseLong(v.getAuditId());
sfSfmxService.renewalBs(aid);
});
}
}
}
}
三、總結(jié)
該定時(shí)任務(wù)每10秒執(zhí)行一次,將滿足條件的審方信息發(fā)送到三方系統(tǒng),并根據(jù)返回的結(jié)果進(jìn)行相應(yīng)的日志記錄和數(shù)據(jù)更新操作。再調(diào)用三方接口時(shí),使用的是封裝好了的工具類將post請求發(fā)送給三方接口,并對https安全傳輸協(xié)議做了跳過操作。
四、RestTemplate
Spring給我們提供了一個(gè)RestTemplate的API,可以方便的實(shí)現(xiàn)Http請求的發(fā)送。其中提供了大量的方法,方便我們發(fā)送Http請求,例如:
可以看到常見的Get、Post、Put、Delete
請求都支持,如果請求參數(shù)比較復(fù)雜,還可以使用exchange
方法來構(gòu)造請求。
先將RestTemplate注冊為一個(gè)Bean:
<!--okhttp-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.3.1</version>
</dependency>
配置文件:
ok:
http:
connect-timeout: 1
read-timeout: 3
write-timeout: 3
# 連接池中整體的空閑連接的最大數(shù)量
max-idle-connections: 200
# 連接空閑時(shí)間最多為 300 秒
keep-alive-duration: 300
package com.jzj.config;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
/**
* 描述:RestTemplate配置
* 創(chuàng)建人: 黎明
* 創(chuàng)建時(shí)間: 2023/11/17
* 版本: 1.0.0
*/
@Configuration
public class RestTemplateConfig {
@Value("${ok.http.connect-timeout}")
private Integer connectTimeout;
@Value("${ok.http.read-timeout}")
private Integer readTimeout;
@Value("${ok.http.write-timeout}")
private Integer writeTimeout;
@Value("${ok.http.max-idle-connections}")
private Integer maxIdleConnections;
@Value("${ok.http.keep-alive-duration}")
private Long keepAliveDuration;
@Bean
public RestTemplate restTemplate() {
ClientHttpRequestFactory factory = httpRequestFactory();
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
public ClientHttpRequestFactory httpRequestFactory() {
return new OkHttp3ClientHttpRequestFactory(okHttpConfigClient());
}
public OkHttpClient okHttpConfigClient(){
return new OkHttpClient().newBuilder()
.connectionPool(pool())
.connectTimeout(connectTimeout, TimeUnit.SECONDS)
.readTimeout(readTimeout, TimeUnit.SECONDS)
.writeTimeout(writeTimeout, TimeUnit.SECONDS)
.hostnameVerifier((hostname, session) -> true)
.build();
}
public ConnectionPool pool() {
return new ConnectionPool(maxIdleConnections, keepAliveDuration, TimeUnit.SECONDS);
}
}
遠(yuǎn)程調(diào)用
方式一:
// 2.5 調(diào)用接口下傳商品數(shù)據(jù)
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 2.5.1 設(shè)置請求參數(shù)
HttpEntity<AddGoodsVo> requestEntity = new HttpEntity<>(addGoodsVo, headers);
// 2.5.2 發(fā)送請求
ResponseEntity<ResponseDto> response = restTemplate.exchange(
Request.URL,//請求地址
HttpMethod.POST,//請求方式
requestEntity,//請求參數(shù)
ResponseDto.class //自定義返回值類型
);
// 2.5.3 解析響應(yīng)
if(!response.getStatusCode().is2xxSuccessful()){
// 查詢失敗,直接結(jié)束
log.error("調(diào)用百望接口錯(cuò)誤!!!");
}
方式二:文章來源:http://www.zghlxwxcb.cn/news/detail-651746.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-651746.html
到了這里,關(guān)于SpringBoot案例 調(diào)用第三方接口傳輸數(shù)據(jù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!