.NET Core 下的 API 网关

网关介绍

网关其实就是将我们写好的API全部放在一个统一的地址暴露在公网,提供访问的一个入口。在 .NET Core下可以使用Ocelot来帮助我们很方便的接入API 网关。与之类似的库还有ProxyKit,微软也发布了一个反向代理的库YARP

关于网关的介绍不多说了,网上文章也挺多的,这些都是不错的选择,听说后期Ocelot将会使用YARP来重写。本篇主要实践一下在.NET Core环境下使用Ocelot

  • Ocelot官网:https://threemammals.com/ocelot

  • Ocelot文档:https://ocelot.readthedocs.io

  • GitHub:https://github.com/ThreeMammals/Ocelot

  • Ocelot资源汇总:https://www.cnblogs.com/shanyou/p/10363360.html

接入使用

接口示例

先创建几个项目用于测试,创建两个默认的API项目,Api_A和Api_B,在创建一个网关项目Api_Gateway,网关项目可以选择空的模板。

现在分别在Api_A和Api_B中写几个api,将默认的WeatherForecastController中返回模型WeatherForecast添加一个字段Source,用于区分是哪个API返回的数据。

using System;namespace Api_A
{public class WeatherForecast{public string Source { get; set; } = "Api_A";public DateTime Date { get; set; }public int TemperatureC { get; set; }public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);public string Summary { get; set; }}
}using System;namespace Api_B
{public class WeatherForecast{public string Source { get; set; } = "Api_B";public DateTime Date { get; set; }public int TemperatureC { get; set; }public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);public string Summary { get; set; }}
}

直接使用WeatherForecastController默认方法,在路由中添加api前缀。

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;namespace Api_A.Controllers
{[ApiController][Route("api/[controller]")]public class WeatherForecastController : ControllerBase{private static readonly string[] Summaries = new[]{"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"};[HttpGet]public IEnumerable<WeatherForecast> Get(){var rng = new Random();return Enumerable.Range(1, 5).Select(index => new WeatherForecast{Date = DateTime.Now.AddDays(index),TemperatureC = rng.Next(-20, 55),Summary = Summaries[rng.Next(Summaries.Length)]}).ToArray();}}
}using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;namespace Api_B.Controllers
{[ApiController][Route("api/[controller]")]public class WeatherForecastController : ControllerBase{private static readonly string[] Summaries = new[]{"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"};[HttpGet]public IEnumerable<WeatherForecast> Get(){var rng = new Random();return Enumerable.Range(1, 5).Select(index => new WeatherForecast{Date = DateTime.Now.AddDays(index),TemperatureC = rng.Next(-20, 55),Summary = Summaries[rng.Next(Summaries.Length)]}).ToArray();}}
}

再分别在Api_A和Api_B中添加两个控制器:ApiAController、ApiBController,然后加上几个简单的restful api。

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;namespace Api_A.Controllers
{[Route("api/[controller]")][ApiController]public class ApiAController : ControllerBase{[HttpGet]public IEnumerable<string> Get(){return new string[] { "value1", "value2" };}[HttpGet("{id}")]public string Get(int id){return $"Get:{id}";}[HttpPost]public string Post([FromForm] string value){return $"Post:{value}";}[HttpPut("{id}")]public string Put(int id, [FromForm] string value){return $"Put:{id}:{value}";}[HttpDelete("{id}")]public string Delete(int id){return $"Delete:{id}";}}
}
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;namespace Api_B.Controllers
{[Route("api/[controller]")][ApiController]public class ApiBController : ControllerBase{[HttpGet]public IEnumerable<string> Get(){return new string[] { "value1", "value2" };}[HttpGet("{id}")]public string Get(int id){return $"Get:{id}";}[HttpPost]public string Post([FromForm] string value){return $"Post:{value}";}[HttpPut("{id}")]public string Put(int id, [FromForm] string value){return $"Put:{id}:{value}";}[HttpDelete("{id}")]public string Delete(int id){return $"Delete:{id}";}}
}

方便查看接口,这里添加一下swagger组件,这样我们Api_A和Api_B项目分别就有了6个接口。

接着打包docker镜像,放在docker中运行这两个api项目。这一步可以用任何你熟悉的方式,run起来即可。

docker build -t api_a:dev -f ./Api_A/Dockerfile .
docker build -t api_b:dev -f ./Api_B/Dockerfile .

build成功后,指定两个端口运行api项目。

docker run -d -p 5050:80 --name api_a api_a:dev
docker run -d -p 5051:80 --name api_b api_b:dev

Api_A指定了5050端口,通过 http://localhost:5050/swagger打开可以看到swagger文档界面,Api_B指定了5051端口,通过 http://localhost:5051/swagger打开可以看到swagger文档界面,这样就大功告成了,接下来才是重点将两个api项目配置到Api_Gateway网关项目中。

配置网关

在网关项目Api_Gateway中都添加Ocelot组件包。

Install-Package Ocelot

Ocelot中最关键的就是配置路由信息,新建一个ocelot.json配置文件,将我们的两个API接口匹配规则放进去。

{"Routes": [//ApiA{"DownstreamPathTemplate": "/api/WeatherForecast","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "localhost","Port": 5050}],"UpstreamPathTemplate": "/ApiA/WeatherForecast","UpstreamHttpMethod": [ "Get" ]},{"DownstreamPathTemplate": "/api/ApiA","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "localhost","Port": 5050}],"UpstreamPathTemplate": "/ApiA","UpstreamHttpMethod": [ "Get", "POST" ]},{"DownstreamPathTemplate": "/api/ApiA/{id}","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "localhost","Port": 5050}],"UpstreamPathTemplate": "/ApiA/{id}","UpstreamHttpMethod": [ "Get", "Put", "Delete" ]},//ApiB{"DownstreamPathTemplate": "/api/WeatherForecast","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "localhost","Port": 5051}],"UpstreamPathTemplate": "/ApiB/WeatherForecast","UpstreamHttpMethod": [ "Get" ]},{"DownstreamPathTemplate": "/api/ApiB","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "localhost","Port": 5051}],"UpstreamPathTemplate": "/ApiB","UpstreamHttpMethod": [ "Get", "POST" ]},{"DownstreamPathTemplate": "/api/ApiB/{id}","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "localhost","Port": 5051}],"UpstreamPathTemplate": "/ApiB/{id}","UpstreamHttpMethod": [ "Get", "Put", "Delete" ]}],"GlobalConfiguration": {"BaseUrl": "https://localhost:44335"}
}

关于配置文件中的各项具体含义,可以参考官方文档中的介绍。主要就是将DownstreamPathTemplate模板内容转换为UpstreamPathTemplate模板内容进行接口的访问,同时可以指定HTTP请求的方式等等。GlobalConfiguration中的BaseUrl为我们暴漏出去的网关地址。

设置好ocelot.json后,需要在代码中使用它,在Program.cs中添加配置文件。

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;namespace Api_Gateway
{public class Program{public static void Main(string[] args){CreateHostBuilder(args).Build().Run();}public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureAppConfiguration((context, config) =>{config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);}).ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseStartup<Startup>();});}
}

Startup.cs中使用Ocelot

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;namespace Api_Gateway
{public class Startup{public void ConfigureServices(IServiceCollection services){services.AddOcelot();}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapGet("/", async context =>{await context.Response.WriteAsync("Hello World!");});});app.UseOcelot().Wait();}}
}

完成以上操作后,我们试着去调用接口看看能否正确获取预期数据。

curl -X GET "https://localhost:44335/ApiA"
curl -X GET "https://localhost:44335/ApiB"curl -X POST "https://localhost:44335/ApiA" -H "Content-Type: multipart/form-data" -F "value=ApiA"
curl -X POST "https://localhost:44335/ApiB" -H "Content-Type: multipart/form-data" -F "value=ApiB"curl -X GET "https://localhost:44335/ApiA/12345"
curl -X GET "https://localhost:44335/ApiB/12345"curl -X PUT "https://localhost:44335/ApiA/12345" -H "Content-Type: multipart/form-data" -F "value=ApiA"
curl -X PUT "https://localhost:44335/ApiB/12345" -H "Content-Type: multipart/form-data" -F "value=ApiB"curl -X DELETE "https://localhost:44335/ApiA/12345"
curl -X DELETE "https://localhost:44335/ApiB/12345"curl -X GET "https://localhost:44335/ApiA/WeatherForecast"
curl -X GET "https://localhost:44335/ApiB/WeatherForecast"

可以看到,两个项目中的接口全部可以通过网关项目暴露的地址进行中转,是不是很方便?

本篇只是简单的应用,对于Ocelot的功能远不止于此,它非常强大,还可以实现请求聚合、服务发现、认证、鉴权、限流熔断、并内置了负载均衡器,而且这些功能都是只需要简单的配置即可完成。就不一一描述了,如有实际开发需求和问题,可以查看官方文档和示例。

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

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

相关文章

leetcode剑指 Offer 29. 顺时针打印矩阵

一&#xff1a;题目 二:上码 class Solution { public:vector<int> spiralOrder(vector<vector<int>>& matrix) {//判空处理 否则会出现空指针异常if(matrix.size() 0 || matrix[0].size() 0){return {};}int m matrix.size();// 行int n matrix[0].…

VS也可以这样进行快捷安装

前言记录带新人的那些事&#xff0c;主要是一些工作技巧上的分享部门老大&#xff1a;阿元&#xff0c;明天有几个实习生新人开发来入职&#xff0c;你负责带一下他们阿元&#xff1a;明天吗&#xff1f;哦&#xff0c;好的&#xff0c;顺便问下&#xff1a;男的女的&#xff1…

蓝桥杯-卡片-填空题

一:题目 二:思路 1是最快消耗完的计算1的个数即可 三&#xff1a;上码 #include <iostream> using namespace std; int main() {int ans 0;int count 0;for(int i 1; i < 20000; i) {string str to_string(i);// cout << str << endl;for(int j …

跟我一起学.NetCore之Swagger让前后端不再烦恼及界面自定义

前言随着前后端分离开发模式的流行&#xff0c;接口对接、联调成为常事&#xff0c;前端同事会经常问&#xff1a;我需要调哪个接口&#xff1f;这个接口数据格式是啥&#xff1f;条件都传啥&#xff1f; 对于一些紧急接口可能会采取沟通对接&#xff0c;然后补文档&#xff0c…

由浅入深,带你搞懂 Pytorch 中的张量 tensor 是什么

目录1、tensor 是什么&#xff1f;2、tensor 的三个属性2.1 Rank 秩2.2 Axis&#xff08;复数 为 Axes&#xff09; 轴2.3 Shape 形状3、Pytorch 中 torch.Tensor 的三个属性3.1 torch.dtype3.2 torch.device3.3 torch.layout4、创建张量的两种方法4.1 从现有数据创建张量4.2 凭…

蓝桥杯-排序-填空题

一:题目 二:上码 #include <iostream> using namespace std; int main() {// 请在此输入您的代码/**/**冒泡排序中:我们考虑到最坏的情况,那就是全都是逆序 那么就需要交换 N(N-1)/2; 那么100次 最起码需要 15个字符&#xff0c;而15个字符完全逆序的话 需要交换 105次…

打造钉钉事件分发平台之钉钉审批等事件处理

前言上讲和上上讲我们说到了钉钉的审批和钉钉通讯录的一个简单示例&#xff0c;这次我们讲下如何快速打造一个自己的钉钉事件分发平台。让你能够通过监听用户在钉钉上的操作&#xff0c;然后进行对应的业务处理&#xff0c;比如钉钉流程审批完后业务处理、通讯录员工增加后对应…

Pytorch 中 Dataset 和 DataLoader,以及 torchvision 的 datasets 完全理解

目录1、torch.utils.data.Dataset()2、torch.utils.data.Sampler()3、torch.utils.data.DataLoader()4、torchvision.datasets.ImageFolder()5、例子 torchvision.datasets.FashionMNIST()1、torch.utils.data.Dataset() 首先最基础的&#xff0c;是 torch.utils.data.Dataset…

蓝桥杯-成绩分析-编程题

一:题目 二&#xff1a;上码 #include<bits/stdc.h> using namespace std;int main() {int n;cin >> n;vector<int> v(n,0);for (int i 0; i < n; i) {cin >> v[i];}sort(v.begin(),v.end());double sum accumulate(v.begin(),v.end(),0);double…

BeetleX框架详解-小结

到这里BeetleX组件代码讲解完成了&#xff0c;由于组件只封装了TCP基础通讯的功能&#xff0c;因此在内容上并不会有太多&#xff1b;通以上内容相信对BeetleX的设计有一定的了解&#xff0c;在使用上也更加容易。要点Socket对象应用SocketAsyncEventArgs对象应用线程池的应用与…

深度学习入门笔记 —— 循环神经网络 RNN

首先&#xff0c;明确 RNN 的主要任务是用于文本分类&#xff0c;而解决文本分类任务最经典的模型是词袋模型&#xff08;Bag-of-Words Model&#xff09;&#xff0c;如图所示&#xff0c;输入是三个句子&#xff0c;词袋模型首先要定义一个词汇表 vocabulary&#xff0c;里面…

深圳店匠笔试题-4.01

一:题目类型 10个选择10个填空2道编程题 二&#xff1a;编程题 1&#xff1a;34 在排序数组中查找元素的第一个和最后一个位置 class Solution { public:/**思路:1.分为两种情况 那就是该元素是存在于排序数组当中,该元素不存在该排序数组当中。2.如果元素是存在于排序数组当…

进击吧! Blazor !第二期 回顾

Blazor 是一个 Web UI 框架&#xff0c;可通过 WebAssembly 在任意浏览器中运行 .Net 。Blazor 旨在简化快速的单页面 .Net 浏览器应用的构建过程&#xff0c;它虽然使用了诸如 CSS 和 HTML 之类的 Web 技术&#xff0c;但它使用 C&#xff03;语言和 Razor 语法代替 JavaScrip…

蓝桥杯-长草-代码(BFS)

一:题目 题目描述 小明有一块空地&#xff0c;他将这块空地划分为 nn 行 mm 列的小块&#xff0c;每行和每列的长度都为 1。 小明选了其中的一些小块空地&#xff0c;种上了草&#xff0c;其他小块仍然保持是空地。 这些草长得很快&#xff0c;每个月&#xff0c;草都会向外…

Pytorch 中 LSTM 和 LSTMCell 的区别

LSTM 的官方文档在这里 在例子中&#xff1a; LSTM 函数的参数为输入特征向量的长度 input_size 10、隐藏层向量的长度 hidden_size 20、隐藏层的数量 num_layers 2&#xff1b; 输入 input 的维度是时间/序列长度&#xff08;句子有多少个单词&#xff09; time_steps 5、…

程序员修神之路--缓存架构不够好,系统容易瘫痪

“灵魂拷问缓存能大幅度提高系统性能&#xff0c;也能大幅度提高系统瘫痪几率怎么样防止缓存系统被穿透&#xff1f;缓存的雪崩是不是可以完全避免&#xff1f;前几篇文章我们介绍了缓存的优势以及数据一致性的问题&#xff0c;在一个面临高并发系统中&#xff0c;缓存几乎成了…

Magicodes.IE之花式导出

总体设计Magicodes.IE是一个导入导出通用库&#xff0c;支持Dto导入导出以及动态导出&#xff0c;支持Excel、Word、Pdf、Csv和Html。在本篇教程&#xff0c;笔者将讲述如何使用Magicodes.IE进行花式导出。在本篇教程&#xff0c;笔者主要讲述如何使用IE进行花式导出并满足客户…

京东笔试4.2-19:00随笔

30道选择考察到了数据结构&#xff0c;计网&#xff0c;linux,数据库&#xff0c;java基础&#xff0c;就记得这些了 两道编程题 一道二叉树 一道 动态规划 一道也没AC出来 第一道需要会做的前提是 需要创建二叉树 并给其赋值 然后再谈算法 因为一直刷leetcode&#xff0c;转换…

我又踩坑了!如何为HttpClient请求设置Content-Type标头?

最近在重构认证代码&#xff0c;认证过程相当常规&#xff1a;POST /open-api/v1/user-info?client_id&timstamp&rd12345&sign***&methodhmac content-type: application/json payload: { "token":"AA2917B0-C23D-40AB-A43A-4C4B61CC7C74&qu…

利用数组创建二叉树并赋值

1:二叉树的创建与赋值 (1):前言知识 这里的创建是利用层序序列进行创建,主要就是根节点的坐标为i 的话 那么左节点的坐标为 2i1,右节点的坐标为2i2;开辟一个结构体 struct Node {int val;Node * left;Node * right;Node () : val(-1),left(NULL),right(NULL) {};Node(int x)…