網關其實就是將咱們寫好的API所有放在一個統一的地址暴露在公網,提供訪問的一個入口。在 .NET Core下可使用Ocelot
來幫助咱們很方便的接入API 網關。與之相似的庫還有ProxyKit
,微軟也發佈了一個反向代理的庫YARP
。html
關於網關的介紹很少說了,網上文章也挺多的,這些都是不錯的選擇,據說後期Ocelot
將會使用YARP
來重寫。本篇主要實踐一下在.NET Core環境下使用Ocelot
。git
先建立幾個項目用於測試,建立兩個默認的API項目,Api_A和Api_B,在建立一個網關項目Api_Gateway,網關項目能夠選擇空的模板。github
如今分別在Api_A和Api_B中寫幾個api,將默認的WeatherForecastController
中返回模型WeatherForecast
添加一個字段Source,用於區分是哪一個API返回的數據。web
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前綴。docker
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。shell
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個接口。json
接着打包docker鏡像,放在docker中運行這兩個api項目。這一步能夠用任何你熟悉的方式,run起來便可。api
docker build -t api_a:dev -f ./Api_A/Dockerfile . docker build -t api_b:dev -f ./Api_B/Dockerfile .
build成功後,指定兩個端口運行api項目。bash
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網關項目中。restful
在網關項目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
的功能遠不止於此,它很是強大,還能夠實現請求聚合、服務發現、認證、鑑權、限流熔斷、並內置了負載均衡器,並且這些功能都是隻須要簡單的配置便可完成。就不一一描述了,若有實際開發需求和問題,能夠查看官方文檔和示例。