SpringBoot案例 调用第三方接口传输数据

一、前言

最近再写调用三方接口传输数据的项目,这篇博客记录项目完成的过程,方便后续再碰到类似的项目可以快速上手
项目结构:
在这里插入图片描述

二、编码

这里主要介绍HttpClient发送POST请求工具类和定时器的使用,mvc三层架构编码不做探究

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

#####数据源配置#####
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 数据源专有配置
    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

    #配置监控统计拦截的filters,stat:监控统计、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
  #设置日志级别的节点
  level:
    com:
      jzj: debug

Constast

package com.jzj.common;

public class Constast {
    /**
     * 请求头信息
     */
    public static final String CONTENT_TYPE = "application/json;charset=UTF-8";

    /**
     * 返回状态值
     */
    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发送POST请求工具类
 *
 * @author 黎明
 * @version 1.0
 * @date 2023/8/14 14:18
 */
public class HttpUtils {

    /**
     * 发送post请求
     *
     * @param url       请求url
     * @param jsonParam 请求参数
     * @return 响应数据
     */
    public static String doPostJson(String url, String jsonParam) {
        // 创建一个HttpPost对象,并指定URL
        HttpPost httpPost = new HttpPost(url);
        // 声明一个CloseableHttpResponse对象来接收请求的响应
        CloseableHttpResponse response = null;
        // 创建一个CloseableHttpClient对象。wrapClient方法是自定义的方法,用于构建和配置HttpClient对象
        CloseableHttpClient httpClient = wrapClient(url);
        try {
            // 通过重新赋值的方式为HttpPost对象设置URL
            httpPost = new HttpPost(url);
            // 设置请求头的内容类型。Constast.CONTENT_TYPE表示请求的数据类型
            httpPost.setHeader("Content-type", Constast.CONTENT_TYPE);
            // 创建一个StringEntity对象,用于封装JSON参数。
            StringEntity entity = new StringEntity(jsonParam, "UTF-8");
            // 将实体的内容编码设置为与请求头的内容类型相同
            entity.setContentEncoding(new BasicHeader("Content-type", Constast.CONTENT_TYPE));
            // 将StringEntity对象设置为HttpPost请求的实体
            httpPost.setEntity(entity);
            // 执行HttpPost请求,并将响应赋值给response对象
            response = httpClient.execute(httpPost);
            // 判断响应的状态码是否等于200
            if (response.getStatusLine().getStatusCode() == Constast.OK) {
                // 将响应实体转换为字符串并返回。EntityUtils.toString方法用于读取响应实体的内容。
                return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            }
        } catch (Exception e) {
            throw new RuntimeException("[发送POST请求错误:]" + e.getMessage());
        } finally {
            // 释放连接、关闭响应和关闭HttpClient对象
            try {
                httpPost.releaseConnection();
                response.close();
                if (httpClient != null) {
                    httpClient.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 根据URL的协议来配置HttpClient对象
     * @param url url地址
     * @return CloseableHttpClient
     */
    private static CloseableHttpClient wrapClient(String url) {
        // 使用HttpClientBuilder类创建一个默认的HttpClient对象
        CloseableHttpClient client = HttpClientBuilder.create().build();
        if (url.startsWith("https")) { // 检查URL是否以"https"开头,以确定是否需要使用HTTPS协议
            // 如果URL以"https"开头,调用方法获取配置了HTTPS支持的CloseableHttpClient对象
            client = getCloseableHttpsClients();
        }
        return client;
    }

    /**
     * 创建一个支持HTTPS的CloseableHttpClient对象
     * @return CloseableHttpClient
     */
    private static CloseableHttpClient getCloseableHttpsClients() {
        // 采用绕过验证的方式处理https请求
        SSLClient ssl = new SSLClient();
        SSLContext sslcontext = ssl.createIgnoreVerifySSL();
        // 设置协议http和https对应的处理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);
        // 创建自定义的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;

/**
 * 用于创建一个支持绕过HTTPS验证的SSLContext对象
 *
 * @author 黎明
 * @version 1.0
 * @date 2023/8/14 14:35
 */
public class SSLClient {

    // 使用@SuppressWarnings注解来抑制未使用的警告
    @SuppressWarnings("unused")
    public SSLContext createIgnoreVerifySSL() {
        // 创建套接字对象
        SSLContext sslContext = null;
        try {
            // 指定TLS版本
            sslContext = SSLContext.getInstance("TLSv1.2");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("[创建套接字失败:] " + e.getMessage());
        }
        // 实现X509TrustManager接口,用于绕过验证
        X509TrustManager trustManager = new X509TrustManager() {
            // 该方法用于验证客户端证书
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) throws CertificateException {
            }

            // 该方法用于验证服务器证书
            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                                           String paramString) throws CertificateException {
            }

            // 该方法返回受信任的颁发机构(证书颁发机构)数组。在这里,返回null表示不对颁发机构进行限制
            @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;

/**
 * 定时器任务
 *
 * @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() {
        // 根据视图查询所有bs=0审方信息
        List<TrsfToYk> sfAllInfo = trsfToYkService.findAll();
        if (sfAllInfo.size() != 0) { // 判断是否有数据
            ObjectMapper mapper = new ObjectMapper();
            String requestData = null;
            try {
                requestData = mapper.writeValueAsString(sfAllInfo);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            log.info("发送的数据是:{}", requestData);
            String responseData = HttpUtils.doPostJson(Constast.YK_URL, requestData);
            log.info("响应的数据是:{}", responseData);
            JSONObject responseJson = JSONObject.parseObject(responseData);
            YkResultVo ykResultVo = responseJson.toJavaObject(YkResultVo.class);
            // 判断响应状态是否为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);
                });

                // 更新审方明细表bs字段
                ykResultVo.getData().stream().filter(v -> v.getStatus().equals("200")).forEach(v -> {
                    long aid = Long.parseLong(v.getAuditId());
                    sfSfmxService.renewalBs(aid);
                });
            }
        }
    }
}

三、总结

该定时任务每10秒执行一次,将满足条件的审方信息发送到三方系统,并根据返回的结果进行相应的日志记录和数据更新操作。再调用三方接口时,使用的是封装好了的工具类将post请求发送给三方接口,并对https安全传输协议做了跳过操作。

四、RestTemplate

Spring给我们提供了一个RestTemplate的API,可以方便的实现Http请求的发送。其中提供了大量的方法,方便我们发送Http请求,例如:

在这里插入图片描述

可以看到常见的Get、Post、Put、Delete请求都支持,如果请求参数比较复杂,还可以使用exchange方法来构造请求。

先将RestTemplate注册为一个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
    # 连接池中整体的空闲连接的最大数量
    max-idle-connections: 200
    # 连接空闲时间最多为 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配置
 * 创建人: 黎明
 * 创建时间: 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);
    }
}

远程调用

方式一:

// 2.5 调用接口下传商品数据
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 2.5.1 设置请求参数
HttpEntity<AddGoodsVo> requestEntity = new HttpEntity<>(addGoodsVo, headers);
// 2.5.2 发送请求
ResponseEntity<ResponseDto> response = restTemplate.exchange(
        Request.URL,//请求地址
        HttpMethod.POST,//请求方式
        requestEntity,//请求参数
        ResponseDto.class //自定义返回值类型
);
// 2.5.3 解析响应
if(!response.getStatusCode().is2xxSuccessful()){
    // 查询失败,直接结束
    log.error("调用百望接口错误!!!");
}

方式二:

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot 可以通过多种方式调用第三方接口,以下是其中的几种常见方式: 1. 使用 RestTemplate RestTemplate 是 Spring Framework 提供的一个 HTTP 请求工具类,可以方便地发送 HTTP 请求并处理响应结果。具体可以参考以下代码示例: ```java RestTemplate restTemplate = new RestTemplate(); String result = restTemplate.getForObject("http://api.example.com/getData", String.class); System.out.println(result); ``` 2. 使用 Feign Feign 是一个基于接口的 HTTP 客户端,可以简化调用第三方接口的代码。具体可以参考以下代码示例: ```java @FeignClient(name = "example", url = "http://api.example.com") public interface ExampleClient { @GetMapping("/getData") String getData(); } // 调用接口 @Autowired private ExampleClient exampleClient; public void getExampleData() { String result = exampleClient.getData(); System.out.println(result); } ``` 3. 使用 WebClient WebClient 是 Spring Framework 提供的一个非阻塞的 HTTP 客户端,可以发送 HTTP 请求并处理响应结果。具体可以参考以下代码示例: ```java WebClient webClient = WebClient.create("http://api.example.com"); Mono<String> resultMono = webClient.get().uri("/getData").retrieve().bodyToMono(String.class); resultMono.subscribe(result -> System.out.println(result)); ``` 总的来说,Spring Boot 提供了很多方便的工具类和组件,使得调用第三方接口变得非常容易。开发者可以根据具体的场景选择合适的方式来进行接口调用

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小钟不想敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值