Feign实现动态URL
需求描述
动态URL的需求场景:
有一个异步服务S,它为其他业务(业务A,业务B...)提供异步服务接口,在这些异步接口中执行完指定逻辑之后需要回调相应业务方的接口。
这在诸如风控审核,支付回调等场景中挺常见的。
那么,这个回调业务方接口该怎么实现呢?
首先,需要约定好回调这些业务方接口时的请求方法(通常为POST请求),请求参数格式(通常为JSON格式,方便扩展)和响应消息格式(通常为JSON格式)。
具体调用业务方接口时有2种办法来实现:
1.在服务S的每一个异步接口中都独立写一套回调的逻辑
2.因为回调的方法类型和参数格式是约定好的,所以可以写一个统一的公共回调方法即可
方法1显然不是最优选择,这样做会带来大量重复的代码逻辑,而且非常不利用后期维护和升级。
方法2的实现更加灵活一些,便于扩展。
如下将阐述如何使用Feign框架定义一个公共的回调方法。
具体实现
在Feign中能实现动态URL的基础是框架本身就支持,只需要在接口方法中包含一个java.net.URI
参数,Feign就会将该参数值作为目标主机地址,详见Interface Annotations一节中的“Overriding the Request Line”部分。
如下将分别阐述独立使用Feign和使用Spring Cloud OpenFeign实现定义统一的回调方法。
使用Feign定义统一回调方法
定义统一回调方法:
public interface CallbackAPI {
/**
* 统一回调接口方法,请求消息体格式为JSON,响应消息体格式也为JSON
* @param host 接口主机地址,如:http://localhost:8080,该参数是实现动态URL的关键
* @param path 接口路径,如:/test/hello
* @param queryMap 动态URL参数集合
* @param body 请求消息体对象
* @return
*/
@RequestLine("POST {path}")
@Headers({
"Content-Type: application/json",
"Accept: application/json"
})
Object callback(URI host, @Param("path") String path, @QueryMap Map<String, Object> queryMap, Subject body);
}
调用回调方法:
CallbackAPI callbackAPI = Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.logger(new Slf4jLogger())
.logLevel(Logger.Level.FULL)
.target(CallbackAPI.class, "EMPTY"); // 注意:这里的url参数不能为空字符串,但是可以设置为任意字符串值,在这里设置为“EMPTY”
String uri = "http://localhost:8080";
Map<String, Object> queryMap = new HashMap<>(0);
queryMap.put("k", "v");
Subject body = Subject.builder().id(10).build();
Object result = callbackAPI.callback(URI.create(uri), "/test/simple/post/json", queryMap, body);
System.out.println(result);
详细请求日志如下:
2022-02-14 15:32:13,118 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] ---> POST http://localhost:8080/test/simple/post/json?k=v HTTP/1.1
2022-02-14 15:32:13,118 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] Accept: application/json
2022-02-14 15:32:13,118 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] Content-Length: 14
2022-02-14 15:32:13,118 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] Content-Type: application/json
2022-02-14 15:32:13,118 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback]
2022-02-14 15:32:13,118 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] {
"id": 10
}
2022-02-14 15:32:13,118 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] ---> END HTTP (14-byte body)
2022-02-14 15:32:13,153 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] <--- HTTP/1.1 200 (32ms)
2022-02-14 15:32:13,153 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] connection: keep-alive
2022-02-14 15:32:13,153 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] content-length: 9
2022-02-14 15:32:13,153 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] content-type: application/json
2022-02-14 15:32:13,153 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] date: Mon, 14 Feb 2022 07:32:13 GMT
2022-02-14 15:32:13,153 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] keep-alive: timeout=60
2022-02-14 15:32:13,153 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback]
2022-02-14 15:32:13,153 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] {"id":10}
2022-02-14 15:32:13,153 DEBUG [main] f.Logger [Slf4jLogger.java:72] [CallbackAPI#callback] <--- END HTTP (9-byte body)
显然,动态设置目标主机和接口路径已经成功了。
使用Spring Cloud Feign定义统一回调方法
在Spring Cloud Feign中实现定义统一回调接口方法可以直接使用注解进行标注,非常简洁。
定义统一回调方法:
// 注意:这里的url属性值不能为空字符串,但是可以设置为任意字符串值,在这里设置为“EMPTY”
@FeignClient(value = "CallbackAPI", url = "EMPTY", configuration = CallbackConfiguration.class)
public interface CallbackAPI {
/**
* 统一回调接口方法,请求消息体格式为JSON,响应消息体格式也为JSON
* @param host 接口主机地址,如:http://localhost:8080
* @param path 接口路径,如:/test/hello
* @param queryMap 动态URL参数集合
* @param body 请求消息体对象
* @return
*/
@RequestMapping(value = "{path}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
Object callback(URI host,
@PathVariable("path") String path,
@SpringQueryMap Map<String, Object> queryMap,
@RequestBody Object body);
}
// 回调接口配置
public class CallbackConfiguration {
@Bean
public Encoder feignEncoder() {
return new GsonEncoder();
}
@Bean
public Decoder feignDecoder() {
return new GsonDecoder();
}
@Bean
public Retryer feignRetryer() {
return new Retryer.Default();
}
@Bean
public Logger feignLogger() {
return new Slf4jLogger();
}
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
调用回调方法:
@Autowired
CallbackAPI callbackAPI;
String uri = "http://localhost:8080";
Map<String, Object> queryMap = new HashMap<>(0);
queryMap.put("k", "v");
Subject subject = Subject.builder().id(10).build();
Object result = this.callbackAPI.callback(URI.create(uri), "/test/simple/post/json", queryMap, subject);
详细请求日志如下:
2022-02-14 15:38:38.908 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] ---> POST http://localhost:8080/test/simple/post/json?k=v HTTP/1.1
2022-02-14 15:38:38.908 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] Accept: application/json
2022-02-14 15:38:38.908 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] Content-Length: 14
2022-02-14 15:38:38.908 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] Content-Type: application/json
2022-02-14 15:38:38.908 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback]
2022-02-14 15:38:38.908 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] {
"id": 10
}
2022-02-14 15:38:38.908 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] ---> END HTTP (14-byte body)
2022-02-14 15:38:38.924 DEBUG 20184 --- [ main] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@539c48307 pairs: {POST /test/simple/post/json?k=v HTTP/1.1: null}{Accept: application/json}{Content-Type: application/json}{User-Agent: Java/1.8.0_271}{Host: localhost:8080}{Connection: keep-alive}{Content-Length: 14}
2022-02-14 15:38:38.945 DEBUG 20184 --- [ main] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@681e913c6 pairs: {null: HTTP/1.1 200}{Content-Type: application/json}{Content-Length: 9}{Date: Mon, 14 Feb 2022 07:38:38 GMT}{Keep-Alive: timeout=60}{Connection: keep-alive}
2022-02-14 15:38:38.952 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] <--- HTTP/1.1 200 (37ms)
2022-02-14 15:38:38.952 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] connection: keep-alive
2022-02-14 15:38:38.952 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] content-length: 9
2022-02-14 15:38:38.952 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] content-type: application/json
2022-02-14 15:38:38.952 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] date: Mon, 14 Feb 2022 07:38:38 GMT
2022-02-14 15:38:38.953 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] keep-alive: timeout=60
2022-02-14 15:38:38.953 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback]
2022-02-14 15:38:38.955 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] {"id":10}
2022-02-14 15:38:38.955 DEBUG 20184 --- [ main] feign.Logger : [CallbackAPI#callback] <--- END HTTP (9-byte body)
从日志详情看,在Spring Cloud Feign中同样实现动态URL的效果。
总结
在Feign中实现动态URL的关键在于2点:
1.使用URI类型的参数作为请求目标主机地址
2.将请求路径部分使用表达式进行替换
【参考】
https://www.cnblogs.com/syui-terra/p/14386188.html Feign 动态URL 解决记录
https://blog.csdn.net/kysmkj/article/details/89672952 Feign 访问远程api,动态指定url
Feign实现动态URL的更多相关文章
- Feign 动态URL 解决记录
Feign中使用动态URL请求 (应当是spring-cloud-starter-openfeign,不知道和一般的feign有何差别) 在spring项目下,假设有这样个Feign的消费接口,原来写 ...
- 关于动态URL地址设置静态形式
动态URL地址:http://station.com/index.php?c=play&a=index&id=12345 静态URL地址:http://station.com/play ...
- django"动态网页","动态url","调试方法"
一.动态网页 其实只是每次刷新时,获取最新时间而已 1.urls.py from django.conf.urls import patterns, url, include urlpatterns ...
- 如何利用动态URL提升SEO及处理业务逻辑
如果你正在建设一个新网站或者对现有网站重新设计,我们认为应该将网站的 URL 转换为用户友好的 URL,或搜索引擎友好的 URL,这类 URL 也称为语义 URL(Semantic URL).哪些UR ...
- flask动态url规则
动态URL规则 URL规则可以添加变量部分,也就是件更符合同规则的URL抽象成一个URL模式. @app.route('/item/<id>') def item(id): return ...
- springboot集成shiro和开涛的动态url问题
我出现的问题就是一旦/**=authc不管放到前面还是后面都会把所有的资源全部拦截,css和js都访问不到,只需要把开涛的动态url代码改一下就行了(如上图)
- stark组件之处理函数动态url构造(五)
在这个组件中有内置的4个处理函数,它们都有自己对应的url,那么它们的url是怎么构造的呢? ... urlpatterns = [ re_path('list/$', self.wrapper(se ...
- flask之配置文件的加载和动态url的使用
七行代码实现一个flask app from flask import Flask app = Flask(__name__) @app.route('/') def helloworld(): re ...
- Spring Security 动态url权限控制(三)
一.前言 本篇文章将讲述Spring Security 动态分配url权限,未登录权限控制,登录过后根据登录用户角色授予访问url权限 基本环境 spring-boot 2.1.8 mybatis-p ...
随机推荐
- linux分区(挂载)
主分区: 最多4个扩展分区: 最多一个: 扩展分区+主分区最多4个: 不能存放数据,只能划分逻辑分区逻辑分区: 可格式化.存放数据: 分区编号只能从5开始(1.2.3.4编号为主分区或扩展分区) 所有 ...
- Pytest_定制allure报告(12)
定制报告需要先导入allure模块,再使用以下装饰器方法: feature: 标注主要功能模块. story: 标注feature功能模块下的分支功能. description:在报告中显示用例描述. ...
- for循环题目记录
1.求1000以内的完数 /** * 一个数如果恰好等于它的因子之和,这个数就是完数,找出1000内的所有完数 * @author 努力Coding * @version * @data */ pub ...
- 【Spring专场】「MVC容器」不看源码就带你认识核心流程以及运作原理
前提回顾 之前已经写了很多问斩针对于SpringMVC的的执行原理和核心流程,在此再进行冗余介绍就没有任何意义了,所以我们主要考虑的就是针对于SpringMVC还没但大框架有介绍的相关内容解析分析和说 ...
- 代码审计入门之BlueCMS v1.6 sp1
0x00 前言 作为一名代码审计的新手,网上的大佬们说代码审计入门的话BlueCMS比较好,所以我就拿BlueCMS练练.(本人实在是一枚新手,请大佬们多多赐教) 0x01 环境准备 Phpstudy ...
- Android官方文档翻译 八 2.1Setting Up the Action Bar
Setting Up the Action Bar 建立Action Bar This lesson teaches you to 这节课教给你 Support Android 3.0 and Abo ...
- 【小测试】使用腾讯云上的群集版redis
具体的文档请见:https://cloud.tencent.com/document/product/239/3205 群集版本相当于很多个redis进程构成一个群集,最大支持128个分片(猜测分片就 ...
- 【一个idea】YesSql,一种在经典nosql数据库redis上实现SQL引擎的方案(我就要开历史的倒车)
公众号链接 最高级的红酒,一定要掺上雪碧才好喝. 基于这样的品味,我设计出了一套在经典nosql数据库redis上实现SQL引擎的方法.既然redis号称nosql,而我偏要把SQL加到redis上, ...
- unity3d百度语音+图灵机器人
using NAudio.Wave; using System; using System.Collections; using System.Collections.Generic; using S ...
- Docker 实操
---恢复内容开始--- 一.简介 Linux容器作为一类操作系统层面的虚拟化技术成果,旨在立足于单一Linux主机交付多套隔离性Linux环境.与虚拟机不同,容器系统并不需要运行特定的访客操作系统. ...