網(wǎng)關(guān)介紹

網(wǎng)關(guān)其實就是將我們寫好的API全部放在一個統(tǒng)一的地址暴露在公網(wǎng),提供訪問的一個入口。在 .NET Core下可以使用Ocelot來幫助我們很方便的接入API 網(wǎng)關(guān)。與之類似的庫還有ProxyKit,微軟也發(fā)布了一個反向代理的庫YARP

關(guān)于網(wǎng)關(guān)的介紹不多說了,網(wǎng)上文章也挺多的,這些都是不錯的選擇,聽說后期Ocelot將會使用YARP來重寫。本篇主要實踐一下在.NET Core環(huán)境下使用Ocelot

接入使用

接口示例

先創(chuàng)建幾個項目用于測試,創(chuàng)建兩個默認的API項目,Api_A和Api_B,在創(chuàng)建一個網(wǎng)關(guān)項目Api_Gateway,網(wǎng)關(guān)項目可以選擇空的模板。

現(xiàn)在分別在Api_A和Api_B中寫幾個api,將默認的WeatherForecastController中返回模型WeatherForecast添加一個字段Source,用于區(qū)分是哪個API返回的數(shù)據(jù)。

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網(wǎng)關(guān)項目中。

配置網(wǎng)關(guān)

在網(wǎng)關(guān)項目Api_Gateway中都添加Ocelot組件包。

Install-Package Ocelot

Ocelot中最關(guān)鍵的就是配置路由信息,新建一個ocelot.json配置文件,將我們的兩個API接口匹配規(guī)則放進去。

{
"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"
}
}

關(guān)于配置文件中的各項具體含義,可以參考官方文檔中的介紹。主要就是將DownstreamPathTemplate模板內(nèi)容轉(zhuǎn)換為UpstreamPathTemplate模板內(nèi)容進行接口的訪問,同時可以指定HTTP請求的方式等等。GlobalConfiguration中的BaseUrl為我們暴漏出去的網(wǎng)關(guān)地址。

設(shè)置好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();
}
}
}

完成以上操作后,我們試著去調(diào)用接口看看能否正確獲取預期數(shù)據(jù)。

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"

可以看到,兩個項目中的接口全部可以通過網(wǎng)關(guān)項目暴露的地址進行中轉(zhuǎn),是不是很方便?

本篇只是簡單的應用,對于Ocelot的功能遠不止于此,它非常強大,還可以實現(xiàn)請求聚合、服務(wù)發(fā)現(xiàn)、認證、鑒權(quán)、限流熔斷、并內(nèi)置了負載均衡器,而且這些功能都是只需要簡單的配置即可完成。就不一一描述了,如有實際開發(fā)需求和問題,可以查看官方文檔和示例。

本文章轉(zhuǎn)載微信公眾號@一線碼農(nóng)聊技術(shù)

上一篇:

ASP.NET Core Web API基于RESTFul APIs的集合結(jié)果過濾和分頁

下一篇:

使用ASP.NET Core 3.x 構(gòu)建 RESTful API
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費