SpringCloud实用-OpenFeign 调用三方接口

文章目录

  • 前言
  • 正文
    • 一、项目环境
    • 二、项目结构
      • 2.1 包的含义
      • 2.2 代理的场景
    • 三、完整代码示例
      • 3.1 定义FeignClient
      • 3.2 定义拦截器
      • 3.3 配置类
      • 3.4 okhttp配置
      • 3.5 响应体
        • 3.5.1 天行基础响应
        • 3.5.2 热点新闻响应
      • 3.6 代理类
        • 3.6.1 代理工厂
        • 3.6.2 代理客户端
        • 3.6.3 FeignClient的建造器
    • 四、调用&测试
      • 4.1 配置信息
      • 4.2 测试代码
      • 4.3 调用结果
  • 附录

前言

一般来说我们的项目中难免会涉及到调用三方接口的场景。
以前我们可能用 RestTemplate,或者再用OkHttp优化一下。

但是,在读了本文之后,你将发现使用OpenFeign的 FeignClient来调用三方接口,也是纵享丝滑的。

注意,本文旨在使用FeignClient调用三方接口,不讨论其他情况。比如高版本JDK自带的工具类,或者响应式API。

本文使用FeignClient来调用天行API接口。(https://www.tianapi.com/)
在天行官网注册账号后,可以申请自己想要调用的API接口。拿到key之后即可调用。每天都有免费的使用次数。

正文

一、项目环境

项目使用 Java 17、SpringBoot 3.3.4 、SpringCloud 2023.0.3

<properties><java.version>17</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><spring-boot.version>3.3.4</spring-boot.version><spring-cloud.version>2023.0.3</spring-cloud.version></properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.32</version><optional>true</optional></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.32</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-okhttp --><dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId></dependency><!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-loadbalancer --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency></dependencies><dependencyManagement><dependencies><!-- springboot依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

二、项目结构

在这里插入图片描述

2.1 包的含义

  • com.pine.client.core.tianxing: 定义FeignClient接口;
  • com.pine.client.interceptor:拦截器
  • com.pine.client.config:配置类
  • com.pine.client.proxy:代理相关内容
  • com.pine.client.beans:请求体+响应体

2.2 代理的场景

在这里插入图片描述
假设你现在有内网和外网两种网络环境,应用部署在内网,现在你的应用需要访问外部三方接口,需要开白名单;

但是,一般而言,不会直接给你的应用开启白名单,会统一经过一个代理机进行跳转,也就是给内网中的代理机开启白名单,而你的应用使用它作为代理去访问三方接口。

三、完整代码示例

我这里接入三方接口的是:https://www.tianapi.com/apiview/68

温馨提示:天行API的接口文档不可尽信,有的响应体结构对应不上,建议在开发时,可以先用JsonNode接收,然后看实际的响应结构是什么,再定义对象去接收。

3.1 定义FeignClient

package com.pine.client.core.tianxing;import com.pine.client.beans.TianXingNetHotResponse;
import com.pine.client.config.TianXingRequestConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;/*** 天行feign client** @author pine manage* @since 2024-11-22*/
@FeignClient(name = "tianXing", url = "${rpc.tianxing.url}", configuration = TianXingRequestConfiguration.class)
public interface TianXingFeignClient {@PostMapping(value = "/nethot/index")TianXingNetHotResponse netHot();
}

3.2 定义拦截器

package com.pine.client.interceptor;import feign.RequestInterceptor;
import feign.RequestTemplate;/*** 天行 feign请求拦截器** @author pine manage* @since 2024-11-22*/
public class TianXingRequestInterceptor implements RequestInterceptor {private final String key;public TianXingRequestInterceptor(String key) {this.key = key;}@Overridepublic void apply(RequestTemplate requestTemplate) {// 请求头增加参数 content-typerequestTemplate.header("Content-Type", "application/x-www-form-urlencoded");// 请求参数增加keyrequestTemplate.query("key", key);}
}

3.3 配置类

package com.pine.client.config;import com.pine.client.interceptor.TianXingRequestInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 天行接口请求配置** @author pine manage* @since 2024-11-22*/
@Configuration
public class TianXingRequestConfiguration {@Value("${rpc.tianxing.key}")private String tianXingKey;@Beanpublic TianXingRequestInterceptor tianXingRequestInterceptor() {return new TianXingRequestInterceptor(tianXingKey);}
}

3.4 okhttp配置

package com.pine.client.config;import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;import java.io.IOException;/*** okhttp配置** @author pine manage* @since 2024-11-22*/
@Slf4j
@Configuration
public class OkHttpConfig {@Beanpublic okhttp3.OkHttpClient.Builder okHttpClientBuilder() {return new okhttp3.OkHttpClient.Builder().addInterceptor(new LoggingInterceptor());}/*** okhttp3 请求日志拦截器*/static class LoggingInterceptor implements Interceptor {@NonNull@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();long start = System.nanoTime();log.info(String.format("Sending request %s on %s%n%s",request.url(), chain.connection(), request.headers()));Response response = chain.proceed(request);long end = System.nanoTime();log.info(String.format("Received response for %s in %.1fms%n%s",response.request().url(), (end - start) / 1e6d, response.headers()));return response;}}
}

3.5 响应体

3.5.1 天行基础响应
package com.pine.client.beans;import lombok.Data;import java.io.Serial;
import java.io.Serializable;/*** 天行基础响应类** @author pine manage* @since 2024-11-22*/
@Data
public class TianXingBaseResponse implements Serializable {@Serialprivate static final long serialVersionUID = 4154999614348985895L;private Long code;private String msg;}
3.5.2 热点新闻响应
package com.pine.client.beans;import lombok.Data;
import lombok.EqualsAndHashCode;import java.io.Serial;
import java.io.Serializable;
import java.util.List;/*** 天行热点新闻查询响应** @author pine manage* @since 2024-11-22*/
@EqualsAndHashCode(callSuper = true)
@Data
public class TianXingNetHotResponse extends TianXingBaseResponse implements Serializable {@Serialprivate static final long serialVersionUID = 52499588383169858L;private List<Body> newslist;private String tip;@Datapublic static class Body implements Serializable {@Serialprivate static final long serialVersionUID = -1264805102673130063L;private String brief;private String index;private String keyword;private String trend;}
}

3.6 代理类

注意:代理的使用,我这里没做测试,如果你的应用场景涉及到了,建议先测试下。在使用代理的时候,@FeignClient 注解中的 url参数就要去掉,使用代理的proxiedUrl传入。

3.6.1 代理工厂
package com.pine.client.proxy;import java.net.InetSocketAddress;
import java.net.Proxy;/*** 代理工厂** @author pine manage* @since 2024-11-22*/
public class ProxyFactory {public static Proxy newHttpProxy(String hostname, int port) {return new Proxy(Proxy.Type.HTTP,new InetSocketAddress(hostname, port));}public static Proxy newDirectProxy(String hostname, int port) {return new Proxy(Proxy.Type.DIRECT,new InetSocketAddress(hostname, port));}public static Proxy newSocksProxy(String hostname, int port) {return new Proxy(Proxy.Type.SOCKS,new InetSocketAddress(hostname, port));}
}
3.6.2 代理客户端
package com.pine.client.proxy;import feign.Client;
import feign.Request;
import feign.Response;import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.Proxy;/*** 代理客户端** @author pine manage* @since 2024-11-22*/
public class ProxyClient extends Client.Proxied {public ProxyClient(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier, Proxy proxy) {super(sslContextFactory, hostnameVerifier, proxy);}@Overridepublic Response execute(Request request, Request.Options options) throws IOException {return super.execute(request, options);}}
3.6.3 FeignClient的建造器
package com.pine.client.proxy;import feign.Feign;
import feign.Request;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.cloud.openfeign.DefaultFeignLoggerFactory;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import java.net.Proxy;
import java.time.Duration;
import java.util.Optional;/*** FeignClient建造器** @author pine manage* @since 2024-11-22*/
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class FeignClientBuilder {private static final Long DEFAULT_CONNECT_TIMEOUT = 5L;private static final Long DEFAULT_READ_TIMEOUT = 10L;private static final boolean DEFAULT_FOLLOW_REDIRECTS = true;@Nullableprivate Encoder encoder;@Nullableprivate Decoder decoder;@NonNullprivate Proxy proxy;@Nullableprivate SSLSocketFactory sslContextFactory;@Nullableprivate HostnameVerifier hostnameVerifier;@Nullableprivate Retryer retryer;@Nullableprivate Long connectTimeout;@Nullableprivate Long readTimeout;@Nullableprivate Boolean followRedirects;public <T> T build(Class<T> clazz, String proxiedUrl) {this.connectTimeout = Optional.ofNullable(this.connectTimeout).orElse(DEFAULT_CONNECT_TIMEOUT);this.readTimeout = Optional.ofNullable(this.readTimeout).orElse(DEFAULT_READ_TIMEOUT);this.followRedirects = Optional.ofNullable(this.followRedirects).orElse(DEFAULT_FOLLOW_REDIRECTS);this.retryer = Optional.ofNullable(retryer).orElse(Retryer.NEVER_RETRY);this.encoder = Optional.ofNullable(encoder).orElse(new Encoder.Default());this.decoder = Optional.ofNullable(decoder).orElse(new Decoder.Default());Request.Options options = new Request.Options(Duration.ofSeconds(connectTimeout), Duration.ofSeconds(readTimeout), followRedirects);ProxyClient proxyClient = new ProxyClient(sslContextFactory, hostnameVerifier, proxy);return Feign.builder().client(proxyClient).retryer(this.retryer).options(options).encoder(encoder).decoder(decoder).logger(new DefaultFeignLoggerFactory(null).create(clazz)).contract(new SpringMvcContract()).target(clazz, proxiedUrl);}//    public static void main(String[] args) {
//        com.pine.client.core.tianxing.TianXingFeignClient tianXingFeignClient = new FeignClientBuilder()
//                .setProxy(ProxyFactory.newHttpProxy("代理地址", 1003))
//                .build(com.pine.client.core.tianxing.TianXingFeignClient.class, "localhost");
//    }
}

四、调用&测试

4.1 配置信息

spring:cloud:openfeign:# 启用okhttp配置okhttp:enabled: trueloadbalancer:# 关闭负载重试retry:enabled: falserpc:tianxing:key: 你自己申请的天行keyurl: http://api.tianapi.com

4.2 测试代码

在controller中添加代码:

@Resource
private TianXingFeignClient tianXingFeignClient;@PostMapping("/test")
public ResultVo<TianXingNetHotResponse> test() {TianXingNetHotResponse netHotResponse = tianXingFeignClient.netHot();return ResultVo.success(netHotResponse);
}

4.3 调用结果

控制台输出了okhttp的拦截内容:

2024-11-22 16:04:35.177  INFO  82746 --- [http-nio-8080-exec-1] com.pine.client.config.OkHttpConfig$LoggingInterceptor.intercept(OkHttpConfig.java:39) : [logId=default] Sending request http://api.tianapi.com/nethot/index?key=你自己申请的天行key on null
Accept: */*2024-11-22 16:04:35.477  INFO  82746 --- [http-nio-8080-exec-1] com.pine.client.config.OkHttpConfig$LoggingInterceptor.intercept(OkHttpConfig.java:43) : [logId=default] Received response for http://api.tianapi.com/nethot/index?key=你自己申请的天行key in 298.1ms
Server: nginx
Date: Fri, 22 Nov 2024 08:04:35 GMT
Content-Type: application/json;charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 604800
Strict-Transport-Security: max-age=31536000

接口响应结果:省略(自行调用即可)

附录

附1:本系列文章链接
SpringCloud系列文章目录(总纲篇)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/476586.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《Object类》

目录 一、Object类 1.1 定义与地位 1.2 toString()方法 1.3 equals()方法 1.4 hashcode()方法 一、Object类 1.1 定义与地位 Object类是Java语言中的根类&#xff0c;所有的类&#xff08;除了Object类&#xff09;都直接或间接继承自Object。这就意味着在Java中&#xf…

单头蜗杆铣刀计算——记录一下

前面介绍过一期蜗杆的一些常用的加工方式《蜗杆的加工方式》&#xff0c;其中铣削加工也是非常常见的一种加工方式&#xff0c;下面来看看蜗杆铣刀的由来过程&#xff1a; 首先拿到蜗杆参数之后&#xff0c;需要将蜗杆准确的描述出来。渐开线蜗杆的参数与齿轮基本一致&#xf…

【Flask+Gunicorn+Nginx】部署目标检测模型API完整解决方案

【Ubuntu 22.04FlaskGunicornNginx】部署目标检测模型API完整解决方案 文章目录 1. 搭建深度学习环境1.1 下载Anaconda1.2 打包环境1.3 创建虚拟环境1.4 报错 2. 安装flask3. 安装gunicorn4. 安装Nginx4.1 安装前置依赖4.2 安装nginx4.3 常用命令 5. NginxGunicornFlask5.1 ng…

大数据实战——MapReduce案例实践

&#x1f31f;欢迎来到 我的博客 —— 探索技术的无限可能&#xff01; &#x1f31f;博客的简介&#xff08;文章目录&#xff09; 大数据实战——MapReduce案例实践 一&#xff0e;过程分析&#xff08;截图&#xff09;1. 确定Hadoop处于启动状态2. 在/usr/local/filecotent…

【从零开始的LeetCode-算法】3233. 统计不是特殊数字的数字数量

给你两个 正整数 l 和 r。对于任何数字 x&#xff0c;x 的所有正因数&#xff08;除了 x 本身&#xff09;被称为 x 的 真因数。 如果一个数字恰好仅有两个 真因数&#xff0c;则称该数字为 特殊数字。例如&#xff1a; 数字 4 是 特殊数字&#xff0c;因为它的真因数为 1 和…

java基础概念37:正则表达式2-爬虫

一、定义 【回顾】正则表达式的作用 作用一&#xff1a;校验字符串是否满足规则作用二&#xff1a;在一段文本中查找满足要求的内容——爬虫 二、本地爬虫VS网络爬虫 2-1、本地爬虫 示例&#xff1a; 代码优化&#xff1a; public static void main(String[] args) {// 大…

Flume日志采集系统的部署,实现flume负载均衡,flume故障恢复

目录 安装包 flume的部署 负载均衡测试 故障恢复 安装包 在这里给大家准备好了flume的安装包 通过网盘分享的文件&#xff1a;apache-flume-1.9.0-bin.tar.gz 链接: https://pan.baidu.com/s/1DXMA4PxdDtUQeMB4J62xoQ 提取码: euz7 --来自百度网盘超级会员v4的分享 ----…

SQL注入靶场演练

找闭合&#xff0c;用万能密码&#xff0c;发现过滤or&#xff0c;所以绕过admin’oORr‘1‘’1 发现登陆成功 尝试用order by查询列数&#xff0c;又发现by过滤&#xff0c;所以绕过admin’/**/oorrder/**/bBYy/**/3查出列数是3 用联合查询&#xff0c;发现过滤http://139.1…

【软件入门】Git快速入门

Git快速入门 文章目录 Git快速入门0.前言1.安装和配置2.新建版本库2.1.本地创建2.2.云端下载 3.版本管理3.1.添加和提交文件3.2.回退版本3.2.1.soft模式3.2.2.mixed模式3.2.3.hard模式3.2.4.使用场景 3.3.查看版本差异3.4.忽略文件 4.云端配置4.1.Github4.1.1.SSH配置4.1.2.关联…

日常开发记录-正确的prop传参,reduce搭配promise的使用

日常开发记录-正确的prop传参&#xff0c;reduce搭配promise的使用 1.正确的prop传参2.reduce搭配promise的使用 1.正确的prop传参 一般会的父组件传参子组件 //父组件 <A :demodata.sync"testData" :listData.sync"testData2"></A> data ()…

最大熵谱估计

估计思想&#xff1a;采用最大熵原则&#xff0c;外推自相关函数方法估计信号功率谱。它基于将已知的有限长度自相关序列以外的数据用外推的方法求得&#xff0c; 而不是把它们当作是零。 已知{ R(0),R(1),…R(p)},求得R(p1),R(p2),… 保证外推后自相关矩阵正定&#xff0c;自…

JavaWeb——Mybatis

6. Mybatis MyBatis是一款优秀的持久层框架&#xff0c;用于简化JDBC的开发 6.1. Mybatis入门 6.1.1. 入门程序 6.1.2. JDBC 6.1.3. 数据库连接池 6.1.4. Lombok 6.2. Mybatis基础操作 6.2.1. 删除 6.2.1.1. 根据主键删除 6.2.1.2. 预编译SQL #{id}在编译过程中会替换成?…

零碎04 MybatisPlus自定义模版生成代码

目录 背景 动手开干 需要的依赖包&#xff0c;需要注意mybatis-plus-generator的3.5版本是没有兼容历史版本的。 定义一个CodeGenerator类&#xff0c;负责生成代码和配置属性 Entity模版 背景 MybatisPlus代码生成使用默认的velocity模版解决不了定制化的需求&#xff0…

tomcat 后台部署 war 包 getshell

1. tomcat 后台部署 war 包 getshell 首先进入该漏洞的文件目录 使用docker启动靶场环境 查看端口的开放情况 访问靶场&#xff1a;192.168.187.135:8080 访问靶机地址 http://192.168.187.135:8080/manager/html Tomcat 默认页面登录管理就在 manager/html 下&#xff0c…

MongoDB进阶篇-索引(索引概述、索引的类型、索引相关操作、索引的使用)

文章目录 1. 索引概述2. 索引的类型2.1 单字段索引2.2 复合索引2.3 其他索引2.3.1 地理空间索引&#xff08;Geospatial Index&#xff09;2.3.2 文本索引&#xff08;Text Indexes&#xff09;2.3.3 哈希索引&#xff08;Hashed Indexes&#xff09; 3. 索引相关操作3.1 查看索…

如何给 Apache 新站点目录配置 SELinux ?

在 web 服务器管理领域&#xff0c;确保服务器环境的安全性至关重要。SELinux (Security-Enhanced Linux) 是保护 Linux 服务器最有效的工具之一&#xff0c;它是一种强制访问控制 (MAC mandatory access control) 安全机制。当使用最流行的 web 服务器 Apache 提供 web 内容时…

Elasticsearch 开放推理 API 增加了对 IBM watsonx.ai Slate 嵌入模型的支持

作者&#xff1a;来自 Elastic Saikat Sarkar 使用 Elasticsearch 向量数据库构建搜索 AI 体验时如何使用 IBM watsonx™ Slate 文本嵌入。 Elastic 很高兴地宣布&#xff0c;通过集成 IBM watsonx™ Slate 嵌入模型&#xff0c;我们的开放推理 API 功能得以扩展&#xff0c;这…

【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: 前端 文章目录 &#x1f4af;前言&#x1f4af;案例背景&#x1f4af;逻辑运算符的优先级与短路求值运算符优先级的概念短路求值的概念 &#x1f4af;分析案例代码的执行过程第一步&#xff1a;执行 && 运算第二步&#…

2024年11月21日Github流行趋势

项目名称&#xff1a;twenty 项目维护者&#xff1a;charlesBochet, lucasbordeau, Weiko, FelixMalfait, bosiraphael项目介绍&#xff1a;正在构建一个由社区支持的现代化Salesforce替代品。项目star数&#xff1a;21,798项目fork数&#xff1a;2,347 项目名称&#xff1a;p…

【算法】计算程序执行时间(C/C++)

引言 我们在写算法时要考虑到算法的执行效率&#xff0c;有的题目规定了时间限制&#xff0c;我们需要在这个时间之内去解决问题。如果我们需要比对算法的好坏&#xff0c;就需要输出这个程序运行了多长时间。 在C或C中计算程序执行时间&#xff0c;可以使用多种方法&#xf…