需求描述

动态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的更多相关文章

  1. Feign 动态URL 解决记录

    Feign中使用动态URL请求 (应当是spring-cloud-starter-openfeign,不知道和一般的feign有何差别) 在spring项目下,假设有这样个Feign的消费接口,原来写 ...

  2. 关于动态URL地址设置静态形式

    动态URL地址:http://station.com/index.php?c=play&a=index&id=12345 静态URL地址:http://station.com/play ...

  3. django"动态网页","动态url","调试方法"

    一.动态网页 其实只是每次刷新时,获取最新时间而已 1.urls.py from django.conf.urls import patterns, url, include urlpatterns ...

  4. 如何利用动态URL提升SEO及处理业务逻辑

    如果你正在建设一个新网站或者对现有网站重新设计,我们认为应该将网站的 URL 转换为用户友好的 URL,或搜索引擎友好的 URL,这类 URL 也称为语义 URL(Semantic URL).哪些UR ...

  5. flask动态url规则

    动态URL规则 URL规则可以添加变量部分,也就是件更符合同规则的URL抽象成一个URL模式. @app.route('/item/<id>') def item(id): return ...

  6. springboot集成shiro和开涛的动态url问题

    我出现的问题就是一旦/**=authc不管放到前面还是后面都会把所有的资源全部拦截,css和js都访问不到,只需要把开涛的动态url代码改一下就行了(如上图)

  7. stark组件之处理函数动态url构造(五)

    在这个组件中有内置的4个处理函数,它们都有自己对应的url,那么它们的url是怎么构造的呢? ... urlpatterns = [ re_path('list/$', self.wrapper(se ...

  8. flask之配置文件的加载和动态url的使用

    七行代码实现一个flask app from flask import Flask app = Flask(__name__) @app.route('/') def helloworld(): re ...

  9. Spring Security 动态url权限控制(三)

    一.前言 本篇文章将讲述Spring Security 动态分配url权限,未登录权限控制,登录过后根据登录用户角色授予访问url权限 基本环境 spring-boot 2.1.8 mybatis-p ...

随机推荐

  1. IOS自动化,使用到坐标点击,模拟器的分辨率怎么获取?

    IOS不能通过具体元素定位,就要考虑用坐标来点击,不同的模拟器分辨率不太一样,怎么获取模拟器的分辨率? 可能会想是不是和android模拟器一样,可以直接通过界面看到具体分辨率,不过ios好像不可以直 ...

  2. float类型数据精度问题:12.0f-11.9f=0.10000038,"减不尽"为什么?

    现在我们就详细剖析一下浮点型运算为什么会造成精度丢失? 1.小数的二进制表示问题 首先我们要搞清楚下面两个问题: (1)  十进制整数如何转化为二进制数 算法很简单.举个例子,11表示成二进制数: 1 ...

  3. webSocket 使用 HttpSession 的数据配置与写法

    1.前言 webSoket 无法获取 HttpSession  ,使用就更谈不上了 !!! 2解决过程 使用   configurator  注入即可 (1) 配置一个类 1 package cn.c ...

  4. vue3.0+vite+ts项目搭建-分环境打包(四)

    分环境打包配置 新建.env.dev(或者.env) VITE_NODE_ENV = 'dev' VITE_HOST = 'http://local.host.com' 执行yarn dev ,控制台 ...

  5. kibana7.x安装配置操作elasticsearch

    什么是Kibana? Kibana是一个基于Node.js的Elasticsearch索引库数据统计工具,可以利用Elasticsearch的聚合功能,生成各种图表,如柱形图,线状图,饼图等. 而且还 ...

  6. 使用nginx访问FastDFS fastdfs nginx

    文中所有~~~均为同一个自定义文件夹名字,一般使用项目名称 2.1.为什么需要用Nginx访问? FastDFS通过Tracker服务器,将文件放在Storage服务器存储,但是同组存储服务器之间需要 ...

  7. CODING 携手 Thoughtworks 助力老百姓大药房打造”自治、自决、自动”的敏捷文化

    老百姓大药房是中国具有影响力的药品零售连锁企业,中国药品零售企业综合竞争力百强冠军.中国服务业 500 强企业.湖南省百强企业. 自 2001 年创立以来,现已成功开发了湖南. 陕西.浙江.江苏等 * ...

  8. http://dl-ssl.google.com/android上不去解决方案

    转:https://blog.csdn.net/j04110414/article/details/44149653/ 一. 更新sdk,遇到了更新下载失败问题: Fetching https://d ...

  9. 【刷题-LeetCode】203. Remove Linked List Elements

    Remove Linked List Elements Remove all elements from a linked list of integers that have value *val* ...

  10. IExposedPropertyTable与ExposedReference的使用

    近期回顾Dead of the Book demo时,看见了它们的运用.感觉主要是用于ScriptableObject资源和Scene资源解耦: 并将这类做法规范化. 做一个小测试,IExposedP ...