重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关
重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关
引言
重复造轮子系列是自己平时的一些总结。有的轮子依赖社区提供的轮子为基础,这里把使用过程的一些觉得有意思的做个分享。有些思路或者方法在大神看来可能会比较low。但是能解决实际问题,相信有需要的人也在寻找类似的解决方案。这里可以算作是提供了一种思路,类似问题如果有读者能有更好的解决方案,愿闻其详。
若有阅读后引起内心冲突或者愤怒等不适以及自觉被误导者,不需要切换到抖音等欢乐频道进行综合调理,直接就可以在评论区吐槽。
网关简介
什么是网关,为什么用网关。这些问题网上有很多文章,讲解的非常全面。这里就不做重复的讲解了。
但后面的内容至少需要了解网关下面两点。
API网关是一个服务器,是系统的唯一入口。
API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能(提供监控、鉴权、负载均衡等)。
默认实现
下面演示的项目使用vs2019,Asp.Net Core 2.1开发
1、创建一个ASP.NET Core API项目Agile.Demo1.API,使用Swagger作为在线UI展示
项目结构如图1

图1
发布并且运行,为了方面启动运行,写了个批处理脚本,如图2

图2
直接双击start运行如图3

图3
浏览器打开显示效果如图4

图4
直接Swagger文档在线测试各个接口正常。
2、创建一个ASP.NET Core API项目Agile.Demo2.API 与Agile.Demo1.API项目类似。
3、创建一个基于ocelot的网关服务,项目结构如图5

图5
这里使用Ocelot来做网关,Ocelot是一堆特定顺序的中间件
配置ocelot.json,配置内容如下
{
"ReRoutes": [
//API01 业务接口1
{
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "127.0.0.1",
"Port":
}
],
"UpstreamPathTemplate": "/demo1/{url}",
"UpstreamHttpMethod": [ "Post", "Get" ],
"ReRoutesCaseSensitive": false
},
//API02 业务接口2
{
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "127.0.0.1",
"Port":
}
],
"UpstreamPathTemplate": "/demo2/{url}",
"UpstreamHttpMethod": [ "Post", "Get" ],
"ReRoutesCaseSensitive": false
}
]
}
这个配置比较简单,就配置了两个下游的业务接口。
把两个业务接口站点和网关站点都运行起来,如图6

图6
使用postman直接测试demo1 里面的 saveorder接口,如图7

图7
使用postman直接测试demo2 里面的 saveorder接口,如图8

图8
使用postman通过网关访问demo1,如图9

图9
能正常返回数据,说明网关的转发正常。
通过网关访问demo2也类似,这里就不截图了。下面提供demo代码可以下载自己测试下。
这里只介绍,通过网关的转发,其他网关方面的更多应用不在这里做介绍。
新的问题
有一次,我们提供接口和其他部门对接。按照惯例把接口以及网关部署好,文档提供,让他们按照文档规定的传就可以了。
结果,他们看了文档后提出了疑问,这是什么网关。每个接口请求地址还得拼接出来作为完整的请求,我们代码要做很多调整啊。能不能做成支付宝那种,就一个地址固定不变,然后公共参数,业务参数封装的模式。因为这种模式封装的东西都有现成的,这样我们就不用很大的改动就可以快速对接了。看下支付宝接口,如图10

图10
我想你这公共参数还不是动态的,相当于原来我们提供的网关地址后面加的就是对应的动态数据,道理都一样的啊,但受阿里系影响,他们接口的开发还是对接都是习惯按照支付宝这种模式来的,封装的公共参数什么的都做好了,要调整很麻烦。接口不按照他们的样子来就别扭,增加他们工作量。
当时我想这怎么办,我出接口应该按照我们的要求来啊,但没办法不够强势,还得按照他们阿里系规则来,那就想办法吧。
想到ocelot也是一系列的中间件处理 的,我想那就增加一个中间件,把请求给拦截了,重新组合数据,再下发。
这样可以保证我们内部的调用不变,对外兼容这种请求方式。说干就干,先做个demo试验下能否行得通。
增加一个中间件GatewayMiddleware,代码如下,既然要按照支付宝接口的来,那干脆把公共参数这块整体搬过来。
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class GatewayMiddleware
{
private readonly RequestDelegate _next;
public GatewayMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
//支持类似支付宝的网关模式
//公共请求参数
//app_id method format charset sign_type sign timestamp version app_auth_token biz_content
//请求参数(业务),建议json格式
//trade_no out_trade_no operator_id
if (httpContext.Request.Path.ToString().ToLower() == "/gateway"|| httpContext.Request.Path.ToString().ToLower() == "/gateway.do")
{
//调用方post form提交,获取公共请求参数,处理做转发
if (httpContext.Request.Method.ToLower() == "get")
{
httpContext.Response.ContentType = "text/plain;charset=utf-8";
await httpContext.Response.WriteAsync("调试错误,请回到请求来源地,重新发起请求");
return;
}
var reqForm = httpContext.Request.Form;
if (reqForm == null || reqForm.Count == )
{
httpContext.Response.ContentType = "text/plain;charset=utf-8";
await httpContext.Response.WriteAsync("调试错误,请回到请求来源地,重新发起请求");
return;
}
var app_id = reqForm.ContainsKey("appid") ? reqForm["appid"].ToString() : "";
var method = reqForm.ContainsKey("method") ? reqForm["method"].ToString() : "";//接口名称(格式:模块.控制器.方法) 比如demo1.Values.SaveOrder
var format = reqForm.ContainsKey("format") ? reqForm["format"].ToString() : "json";
var charset = reqForm.ContainsKey("charset") ? reqForm["charset"].ToString() : "utf-8";
var sign_type = reqForm.ContainsKey("sign_type") ? reqForm["sign_type"].ToString() : "md5";
var sign = reqForm.ContainsKey("sign") ? reqForm["sign"].ToString() : "";
var timestamp = reqForm.ContainsKey("timestamp") ? reqForm["timestamp"].ToString() : "";
var version = reqForm.ContainsKey("version") ? reqForm["version"].ToString() : "";
var app_auth_token = reqForm.ContainsKey("app_auth_token") ? reqForm["app_auth_token"].ToString() : "";
var biz_content = reqForm.ContainsKey("biz_content") ? reqForm["biz_content"].ToString() : "";//业务接口参数 json格式
//通过method参数拆分出 模块 控制器 方法
var methods = method.Split('.');
var moduleName = method.Length > ? methods[] : "";
var controllerName = method.Length > ? methods[] : "";
var actionName = method.Length > ? methods[] : "";
//区分有版本和无版本两种情况,version不传或传空就是无版本
var nextPath = string.IsNullOrEmpty(version) ? $"/{moduleName}/api/{controllerName}/{actionName}" : $"/{moduleName}/api/v{version}/{controllerName}/{actionName}";
//下游业务接口暂时只支持post json格式的请求
byte[] postData = Encoding.GetEncoding(charset).GetBytes(biz_content);
httpContext.Request.Path = nextPath;
httpContext.Request.ContentType = "application/json";
httpContext.Request.ContentLength = postData.Length;
httpContext.Request.Body = new MemoryStream(postData);
await _next(httpContext);
}
else
{
await _next(httpContext);
}
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class GatewayMiddlewareExtensions
{
public static void UseGatewayMiddleware(this IApplicationBuilder app)
{
app.UseMiddleware<GatewayMiddleware>();
}
}
Startup.cs增加如下代码,如图11

测试拦截成功,重新组装下发。能够正常返回,测试成功。
具体的操作看代码的说明,这里就不再赘述。
这里有一点要特别说明。因为公共参数是form表单post提交,所以调用方请求过来肯定是post方式。转到下游的时候这个请求类型没有改变,所有暂时只支持下游是post的接口。不过可以增加个参数或者使用format参数值来做区分下游具体是get还是post。因为现在format是json肯定只能是支持post。
测试联调
以访问demo1为例,这里有三种方式访问demo1,使用postman测试如下
1、直接访问,如上面图7
2、通过网关转发方式1,如上面图9
3、通过网关转发方式2,如图12

图12
使用这种方式还是有优势的,比如参数签名这块就可以从业务里面独立出来,在网关处理了。
总结
说服不了,就多干活,多想方案。
由于一个文件最大10M,这里拆开上传
APIDemo1代码下载,APIDemo2代码下载,APIGateway代码下载
感谢阅读,希望这篇文章能给你带来帮助!
重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关的更多相关文章
- 重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印
重复造轮子系列——基于FastReport设计打印模板实现桌面端WPF套打和商超POS高度自适应小票打印 一.引言 桌面端系统经常需要对接各种硬件设备,比如扫描器.读卡器.打印机等. 这里介绍下桌面端 ...
- 重复造轮子系列--内存池(C语言)
这个代码是我上个公司工作项目的里面内存管理(基于伙伴算法)的一个简化又简化的版本. 因为没有内存边界检查: 因为没有内存使用统计: 因为没有考虑线程安全: 因为没有内存分配操作的具体文件位置信息: 因 ...
- 重复造轮子系列--dijkstra算法
前年一时脑热(理想很丰满,现实很骨感),写了这个最短路径优先的低效版本,且留着回忆吧. spf.h #ifndef SPF_H_ #define SPF_H_ typedef struct { int ...
- 重复造轮子系列--字符串处理(C语言)
这些字符代码是以前写的,源于很久很久以前的一个VC++项目,在当时的部门编程比赛里因为用了项目代码的xsplit函数,万万没想到,那个做了几年的项目里面居然有坑..xsplit函数居然不能split连 ...
- GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。
1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便 ...
- 避免重复造轮子的UI自动化测试框架开发
一懒起来就好久没更新文章了,其实懒也还是因为忙,今年上半年的加班赶上了去年一年的加班,加班不息啊,好了吐槽完就写写一直打算继续的自动化开发 目前各种UI测试框架层出不穷,但是万变不离其宗,驱动PC浏览 ...
- 第27篇 重复造轮子---模拟IIS服务器
在写程序的时候,重复造轮子是程序员的一个大忌,很多人对重复造轮子持有反对的态度,但是我觉得这个造轮子的过程,是对于现有的知识的一个深入的探索的过程,虽然我们不可能把轮子造的那么的完善,对于现在有的东西 ...
- 动手造轮子:基于 Redis 实现 EventBus
动手造轮子:基于 Redis 实现 EventBus Intro 上次我们造了一个简单的基于内存的 EventBus,但是如果要跨系统的话就不合适了,所以有了这篇基于 Redis 的 EventBus ...
- 54 个官方 Spring Boot Starters 出炉!别再重复造轮子了…….
在之前的文章,栈长介绍了 Spring Boot Starters,不清楚的可以点击链接进去看下. 前段时间 Spring Boot 2.4.0 也发布了,本文栈长再详细总结下最新的 Spring B ...
随机推荐
- 利用Rsync同步工具上传、删除目标文件
Rsync是文件备份工具,当然也可以当做传输工具,管理远程服务器的文件 上传 rsync -avzP --progress --port 9106 /path/.../指定文件 root@192.16 ...
- cloudsim 3.0.3下载与安装教程
1.配置jdk(之前的文章都讲解过,这里就不具体说了) 2.安装eclipse或MyEclipse 3.下载cloudsim压缩包,这个的官网是需要FQ,这里贴上我的下载地址 链接:https://p ...
- STL-空间配置器、迭代器、traits编程技巧
目录 内存分配和释放 对象的构造和析构 traits要解决的问题 内嵌类别声明解决非指针迭代器的情况 使用模板特例化解决普通指针的情况 迭代器相应类别 内存分配和释放 STL中有两个分配器,一级分配器 ...
- Java 诞生的趣事
Java 命名的由来 Java是印度尼西亚爪哇岛的英文名称,因盛产咖啡而闻名.Java语言中的许多库类名称,多与咖啡有关:如JavaBeans(咖啡豆).NetBeans(网络豆)以及Object ...
- Scala 学习之路(二)—— 基本数据类型和运算符
一.数据类型 1.1 类型支持 Scala 拥有下表所示的数据类型,其中Byte.Short.Int.Long和Char类型统称为整数类型,整数类型加上Float和Double统称为数值类型.Scal ...
- AbstractQueuedSynchronizer(AQS)源码解析
关于AQS的源码解析,本来是没有打算特意写一篇文章来介绍的.不过在写本学期课程作业中,有一门写了关于AQS的,而且也画了一些相关的图,所以直接拿过来分享一下,如有错误欢迎指正. ...
- 汇编入门三-CPU工作原理
本文为读书笔记,个人总结与摘抄自<汇编语言 第二版> 1.CPU从内存中读取数据,首先要获得存储单元的地址. 2.指明进行的操作,如存储或者读写 所以,CPU要进行操作总结为: 1.存储单 ...
- K8s集群部署(四)------ Flannel网络部署
所有节点都要部署Flannel网络,在所有节点操作. 1.为Flannel生成证书 [root@k8s-master ssl]# pwd /usr/local/src/ssl [root@k8s-ma ...
- ubuntu16.04基本设置
1. ubuntu16.04开启ssh https://jingyan.baidu.com/article/f54ae2fc6f9eef1e93b8497a.html 2. windows 远程桌面连 ...
- RDS数据库磁盘满导致实例锁定
问题描述: 阿里云RDS空间不足,进行报警.收到报警后.对数据库中不重要的数据备份后执行delete删除操作.执行成功后发现数据删掉了.但是数据库的空间并没有释放.数据占用空间反而越来越大,最后RDS ...