Ocelot使用與設(shè)置路由Routing
一、安裝Ocelot
在程序包管理器控制臺輸入以下命令安裝Ocelot
Install-Package Ocelot
二、新建兩個項目
我們新建兩個.Net Core WebAPI項目如下:
?
直接就是最初始化的項目,只是我們在ExternalGateway
項目中安裝Ocelot
,并且添加一個ocelot.json
文件(也可以添加多個配置文件然后合并),文件內(nèi)容如下:
{ ?"GlobalConfiguration": { ? ?"BaseUrl": "https://localhost:5000" ?}, ?"Routes": [ ? ?{ ? ? ?"DownstreamPathTemplate": "/{everything}", ? ? ?"DownstreamScheme": "https", ? ? ?"DownstreamHostAndPorts": [ ? ? ? ?{ ? ? ? ? ?"Host": "localhost", ? ? ? ? ?"Port": 5001 ? ? ? ?} ? ? ?], ? ? ?"UpstreamPathTemplate": "/api/{everything}", ? ? ?"UpstreamHttpMethod": [ "Get", "Post" ] ? ?} ?] }
然后注入Ocelot
的服務(wù)和配置請求管道
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
builder.Services.AddOcelot();
//......
app.UseOcelot().Wait();
?然后我們運行兩個項目就能通過ExternalGateway
項目地址訪問uthServer
的地址
?
?三、Routing的參數(shù)配置說明
1、路由
? ?{ ? ? ?"DownstreamPathTemplate": "/{everything}", ? ? ?"DownstreamScheme": "https", ? ? ?"DownstreamHostAndPorts": [ ? ? ? ?{ ? ? ? ? ?"Host": "localhost", ? ? ? ? ?"Port": 5001 ? ? ? ?} ? ? ?], ? ? ?"UpstreamPathTemplate": "/api/{everything}", ? ? ?"UpstreamHttpMethod": [ "Get", "Post" ] ? ?}
其中DownstreamPathTemplate
、DownstreamScheme
和DownstreamHostAndPorts
定義了請求將被轉(zhuǎn)發(fā)到的URL。
參數(shù)DownstreamHostAndPorts
是一個集合定義了請求轉(zhuǎn)到任何下游服務(wù)的主機(jī)和端口,一般是單個即可,但是如果存在負(fù)載均衡到下游服務(wù)那就需要填寫多個,并進(jìn)行相關(guān)的負(fù)載均衡的配置。
參數(shù)UpstreamPathTemplate
是標(biāo)識用于給定下游請求的DownstreamPathTemplate
對應(yīng)的URL
。
參數(shù)UpstreamHttpMethod
便于區(qū)分不同的HTTP的請求到相同的
URL
,可以設(shè)置為空允許全部的請求。
在Ocelot
中可以以{something}
的形式為模板中的變量添加占位符,但是該占位符變量必須同時出現(xiàn)在DownstreamPathTemplate
和UpstreamPathTemplate
的上下游配置中,Ocelot
會嘗試進(jìn)行占位符值的替換。
默認(rèn)請求路徑的配置是不區(qū)分大小寫的,如果需要修改通過以下參數(shù)配置:
"RouteIsCaseSensitive": true
2、全部捕獲
在Ocelot
中還支持捕獲全部路徑的路由,用戶可以指定他們想要匹配的所有流量。
像是如下的配置,所有的請求都將被代理。但是占位符{url}
的名稱不重要,可以使用任何名稱。
{ ? ?"DownstreamPathTemplate": "/{url}", ? ?"DownstreamScheme": "https", ? ?"DownstreamHostAndPorts": [ ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?"Host": "localhost", ? ? ? ? ? ? ? ?"Port": 80, ? ? ? ? ? ?} ? ? ? ?], ? ?"UpstreamPathTemplate": "/{url}", ? ?"UpstreamHttpMethod": [ "Get" ] }
但是全部捕獲的優(yōu)先級是最低的,如果存在其他配置,將會優(yōu)先于此匹配。
3、優(yōu)先級
可以通過Priorty
參數(shù)來這是匹配上游請求路徑的優(yōu)先級,如下配置的情況下請求地址為/goods/delete
的時候優(yōu)先匹配/goods/{catchAll}
,因為0是最低的優(yōu)先級,Priorty
越大優(yōu)先級越高。
{ ? ?"UpstreamPathTemplate": "/goods/{catchAll}" ? ?"Priority": 0} { ? ?"UpstreamPathTemplate": "/goods/delete" ? ?"Priority": 1}
4、上游主機(jī)
參數(shù)UpstreamHost
允許我們設(shè)置該路由的上游主機(jī),配置后僅當(dāng)請求頭的主機(jī)為我們的配置值,才會匹配該路由配置。如果沒有配置UpstreamHost
那就是任何主機(jī)頭都可以。
{ ? ?"DownstreamPathTemplate": "/", ? ?"DownstreamScheme": "https", ? ?"DownstreamHostAndPorts": [ ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?"Host": "10.0.10.1", ? ? ? ? ? ? ? ?"Port": 80, ? ? ? ? ? ?} ? ? ? ?], ? ?"UpstreamPathTemplate": "/", ? ?"UpstreamHttpMethod": [ "Get" ], ? ?"UpstreamHost": "somedomain.com"}
如上述代碼僅當(dāng)主機(jī)頭為somedomain.com
的請求,才會匹配上述路由。如果存在兩個相同的路由配置,但是一個設(shè)置了UpstreamHost
一個沒有設(shè)置,這樣會匹配設(shè)置了的路由。
5、查詢字符串
在Ocelot
中允許將查詢字符串作為DownstreamPathTemplate
的一部分,如下所示上游路徑模板中的{unitId}
將作為下游路徑模板中的查詢字符串參數(shù)unitId
的值。
{ ? ?"Routes": [ ? ? ? ?{ ? ? ? ? ? ?"DownstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", ? ? ? ? ? ?"UpstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates", ? ? ? ? ? ?"UpstreamHttpMethod": [ ? ? ? ? ? ? ? ?"Get" ? ? ? ? ? ?], ? ? ? ? ? ?"DownstreamScheme": "http", ? ? ? ? ? ?"DownstreamHostAndPorts": [ ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ?"Host": "localhost", ? ? ? ? ? ? ? ? ? ?"Port": 50110 ? ? ? ? ? ? ? ?} ? ? ? ? ? ?] ? ? ? ?} ? ?], ? ?"GlobalConfiguration": { ? ?} }
此外Ocelot
還允許將查詢字符串放置在UpstreamPathTemplate
中,以便將某些查詢匹配到對應(yīng)的服務(wù),如下所示只能匹配查詢參數(shù)為unitId
的請求。
{ ? ?"Routes": [ ? ? ? ?{ ? ? ? ? ? ?"DownstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates", ? ? ? ? ? ?"UpstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", ? ? ? ? ? ?"UpstreamHttpMethod": [ ? ? ? ? ? ? ? ?"Get" ? ? ? ? ? ?], ? ? ? ? ? ?"DownstreamScheme": "http", ? ? ? ? ? ?"DownstreamHostAndPorts": [ ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ?"Host": "localhost", ? ? ? ? ? ? ? ? ? ?"Port": 50110 ? ? ? ? ? ? ? ?} ? ? ? ? ? ?] ? ? ? ?} ? ?], ? ?"GlobalConfiguration": { ? ?} }
?四、聚合路由
在Ocelot
中允許使用聚合路由,聚合路由就是將多個路由的結(jié)果結(jié)合成一個進(jìn)行返回。
首先我們將ocelot.json
改為下面的配置,可以看到兩個路由下各自有自己的Key
,然后多了一個聚合路由Aggregates
里面設(shè)置了對應(yīng)的兩個Key
,并且該聚合路由的路徑也被設(shè)置為了/getweatherforecastaggregate/{everything}
{ ?"GlobalConfiguration": { ? ?"BaseUrl": "https://localhost:5000" ?}, ?"Routes": [ ? ?{ ? ? ?"DownstreamPathTemplate": "/{everything}", ? ? ?"DownstreamScheme": "https", ? ? ?"DownstreamHostAndPorts": [ ? ? ? ?{ ? ? ? ? ?"Host": "localhost", ? ? ? ? ?"Port": 5001 ? ? ? ?} ? ? ?], ? ? ?"UpstreamPathTemplate": "/api/{everything}", ? ? ?"UpstreamHttpMethod": [ "Get", "Post" ], ? ? ?"Key": "WeatherForecast1" ? ?}, ? ?{ ? ? ?"DownstreamPathTemplate": "/{everything}", ? ? ?"DownstreamScheme": "https", ? ? ?"DownstreamHostAndPorts": [ ? ? ? ?{ ? ? ? ? ?"Host": "localhost", ? ? ? ? ?"Port": 5001 ? ? ? ?} ? ? ?], ? ? ?"UpstreamPathTemplate": "/api2/{everything}", ? ? ?"UpstreamHttpMethod": [ "Get", "Post" ], ? ? ?"Key": "WeatherForecast2" ? ?} ?], ?"Aggregates": [ ? ?{ ? ? ?"RouteKeys": [ ? ? ? ?"WeatherForecast1", ? ? ? ?"WeatherForecast2" ? ? ?], ? ? ?"UpstreamPathTemplate": "/getweatherforecastaggregate/{everything}" ? ?} ?] }
然后我們請求對應(yīng)的地址就可以看到返回了以兩個Key為鍵的對應(yīng)路由地址接口返回的信息,如果接口報錯則返回空。這里需要注意,聚合路由Aggregates
中的上游路徑UpstreamPathTemplate
其實對應(yīng)的就是Route
中的UpstreamPathTemplate
,也就是說路由中的上游路徑就是聚合路由的下游路徑,對應(yīng)的變量占位符啥的都會傳遞。
?如果我們不滿意返回的結(jié)果可以自定義聚合路由的來處理返回的結(jié)果,我們現(xiàn)在將oeclot.json
中的聚合路由修改如下增加Aggregator
參數(shù)
?"Aggregates": [ ? ?{ ? ? ?"RouteKeys": [ ? ? ? ?"WeatherForecast1", ? ? ? ?"WeatherForecast2" ? ? ?], ? ? ?"UpstreamPathTemplate": "/getweatherforecastaggregate/{everything}", ? ? ?"Aggregator": "MyAggregator" ? ?} ?]
然后我們創(chuàng)建一個與Aggregator
參數(shù)同名的重寫類,并且繼承IDefinedAggregator
接口重寫Aggregate(List<HttpContext> responses)
方法如下:
? ?public class MyAggregator: IDefinedAggregator ? ?{ ? ? ? ?public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses) ? ? ? ?{ ? ? ? ? ? ?var one = await ((DownstreamResponse)responses[0].Response.HttpContext.Items["DownstreamResponse"]).Content.ReadAsStringAsync(); ? ? ? ? ? ?//Response.Body不能獲取,只能通過HttpContext.Items ? ? ? ? ? ?//using var resReader1 = new StreamReader(responses[0].Response.Body); ? ? ? ? ? ?//var one = await resReader1.ReadToEndAsync(); ? ? ? ? ? ?var two = await ((DownstreamResponse)responses[1].Response.HttpContext.Items["DownstreamResponse"]).Content.ReadAsStringAsync(); ? ? ? ? ? ?var my = $"\"{Guid.NewGuid()}\":{{comment:\"我是自定義聚合器返回內(nèi)容\"}}"; ? ? ? ? ? ?var merge = $"{one}, {two},{my}"; ? ? ? ? ? ?List<Header> headers = new List<Header>(); ? ? ? ? ? ?return await Task.FromResult(new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers, "some reason")); ? ? ? ?} ? ?}
然后在注冊該類,可以是瞬態(tài)注冊等此處單例
builder.Services.AddOcelot().AddSingletonDefinedAggregator<MyAggregator>();
然后我們訪問地址就可看到返回了我們添加的內(nèi)容
?
?需要注意的是,聚合路由只支持Get
請求聚合,并且下游服務(wù)返回如果是404
則返回空,只返回json
字符串。