让 Ocelot 与 asp.net core “共存”
Intro
我們的 API 之前是一個(gè)單體應(yīng)用,各個(gè)模塊的服務(wù)是通過(guò) Assembly 集成在一起,最后部署在一個(gè) web server 下的。
我們已經(jīng)在拆分服務(wù)并且在 Ocelot 的基礎(chǔ)上封裝了我們自己的網(wǎng)關(guān),但是服務(wù)還沒(méi)有完全拆分,于是有這么一個(gè)需求,對(duì)于 Ocelot 配置的路由去交給 Ocelot 去轉(zhuǎn)發(fā)到真正的服務(wù)地址,而那些 Ocelot 沒(méi)有定義的路由則讓交給 AspNetCore 去處理。
實(shí)現(xiàn)原理
實(shí)現(xiàn)原理是讓 Ocelot 作為一個(gè)動(dòng)態(tài)分支路由,只有當(dāng) Ocelot 配置了對(duì)應(yīng)路由的下游地址才走 Ocelot 的分支,才把請(qǐng)求交給 Ocelot 處理。
我們可以使用 MapWhen 來(lái)處理,接下來(lái)就需要知道怎么樣判斷 Ocelot 是否配置了某一個(gè)路由,Ocelot 內(nèi)部的處理管道,在向下游請(qǐng)求之前是要找到對(duì)應(yīng)匹配的下游路由,所以我們?nèi)タ匆豢?Ocelot 的源碼,看看 Ocelot 內(nèi)部是怎么找下游路由的,Ocelot 找下游路由中間件源碼
public async Task Invoke(DownstreamContext context)
{
var upstreamUrlPath = context.HttpContext.Request.Path.ToString();
var upstreamQueryString = context.HttpContext.Request.QueryString.ToString();
var upstreamHost = context.HttpContext.Request.Headers["Host"];
Logger.LogDebug($"Upstream url path is {upstreamUrlPath}");
var provider = _factory.Get(context.Configuration);
// 獲取下游路由
var downstreamRoute = provider.Get(upstreamUrlPath, upstreamQueryString, context.HttpContext.Request.Method, context.Configuration, upstreamHost);
if (downstreamRoute.IsError)
{
Logger.LogWarning($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}");
SetPipelineError(context, downstreamRoute.Errors);
return;
}
var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value));
Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");
context.TemplatePlaceholderNameAndValues = downstreamRoute.Data.TemplatePlaceholderNameAndValues;
await _multiplexer.Multiplex(context, downstreamRoute.Data.ReRoute, _next);
}
通過(guò)上面的源碼,我們就可以判斷 Ocelot 是否有與請(qǐng)求相匹配的下游路由信息
實(shí)現(xiàn)
既然找到了 Ocelot 如何找下游路由,就先給 Ocelot 加一個(gè)擴(kuò)展吧,實(shí)現(xiàn)代碼如下,Ocelot 擴(kuò)展完整代碼
public static IApplicationBuilder UseOcelotWhenRouteMatch(this IApplicationBuilder app,
Action<IOcelotPipelineBuilder, OcelotPipelineConfiguration> builderAction)
=> UseOcelotWhenRouteMatch(app, builderAction, new OcelotPipelineConfiguration());
public static IApplicationBuilder UseOcelotWhenRouteMatch(this IApplicationBuilder app,
Action<OcelotPipelineConfiguration> pipelineConfigurationAction,
Action<IOcelotPipelineBuilder, OcelotPipelineConfiguration> builderAction)
{
var pipelineConfiguration = new OcelotPipelineConfiguration();
pipelineConfigurationAction?.Invoke(pipelineConfiguration);
return UseOcelotWhenRouteMatch(app, builderAction, pipelineConfiguration);
}
public static IApplicationBuilder UseOcelotWhenRouteMatch(this IApplicationBuilder app, Action<IOcelotPipelineBuilder, OcelotPipelineConfiguration> builderAction, OcelotPipelineConfiguration configuration)
{
app.MapWhen(context =>
{
// 獲取 OcelotConfiguration
var internalConfigurationResponse =
context.RequestServices.GetRequiredService<IInternalConfigurationRepository>().Get();
if (internalConfigurationResponse.IsError || internalConfigurationResponse.Data.ReRoutes.Count == 0)
{
// 如果沒(méi)有配置路由信息,不符合分支路由的條件,直接退出
return false;
}
var internalConfiguration = internalConfigurationResponse.Data;
var downstreamRouteFinder = context.RequestServices
.GetRequiredService<IDownstreamRouteProviderFactory>()
.Get(internalConfiguration);
// 根據(jù)請(qǐng)求以及上面獲取的Ocelot配置獲取下游路由
var response = downstreamRouteFinder.Get(context.Request.Path, context.Request.QueryString.ToString(),
context.Request.Method, internalConfiguration, context.Request.Host.ToString());
// 如果有匹配路由則滿足該分支路由的條件,交給 Ocelot 處理
return !response.IsError
&& !string.IsNullOrEmpty(response.Data?.ReRoute?.DownstreamReRoute?.FirstOrDefault()
?.DownstreamScheme);
}, appBuilder => appBuilder.UseOcelot(builderAction, configuration).Wait());
return app;
}
使用
在 Startup 里
ConfigurationServices 配置 mvc 和 Ocelot
Configure 方法里配置 ocelot 和 mvc
app.UseOcelotWhenRouteMatch((ocelotBuilder, pipelineConfiguration) =>
{
// This is registered to catch any global exceptions that are not handled
// It also sets the Request Id if anything is set globally
ocelotBuilder.UseExceptionHandlerMiddleware();
// This is registered first so it can catch any errors and issue an appropriate response
ocelotBuilder.UseResponderMiddleware();
ocelotBuilder.UseDownstreamRouteFinderMiddleware();
ocelotBuilder.UseDownstreamRequestInitialiser();
ocelotBuilder.UseRequestIdMiddleware();
ocelotBuilder.UseMiddleware<ClaimsToHeadersMiddleware>();
ocelotBuilder.UseLoadBalancingMiddleware();
ocelotBuilder.UseDownstreamUrlCreatorMiddleware();
ocelotBuilder.UseOutputCacheMiddleware();
ocelotBuilder.UseMiddleware<HttpRequesterMiddleware>();
// cors headers
ocelotBuilder.UseMiddleware<CorsMiddleware>();
});
app.UseMvc();
新建一個(gè) TestController
[Route("/api/[controller]")]
public class TestController : ControllerBase
{
public IActionResult Get()
{
return Ok(new
{
Tick = DateTime.UtcNow.Ticks,
Msg = "Hello Ocelot",
});
}
}
具體代碼可以參考這個(gè) 網(wǎng)關(guān)示例項(xiàng)目
示例項(xiàng)目的 Ocelot 配置是存在 Redis 里面的,配置的 ReRoutes 如下:
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api.php?key=free&appid=0&msg={everything}",
"UpstreamPathTemplate": "/api/chat/{everything}",
"UpstreamHttpMethod": [
"Get",
"POST",
"PUT",
"PATCH",
"DELETE",
"OPTIONS"
],
"AddHeadersToRequest": {
},
"RequestIdKey": "RequestId",
"ReRouteIsCaseSensitive": false,
"ServiceName": "",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "api.qingyunke.com",
"Port": 80
}
],
"DangerousAcceptAnyServerCertificateValidator": false
}
],
"GlobalConfiguration": {
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
"UseCookieContainer": false,
"UseTracing": false
}
}
}
運(yùn)行項(xiàng)目進(jìn)行測(cè)試:
訪問(wèn) Ocelot 定義的路由 http://localhost:65125/api/chat/hello ,返回信息如圖所示:
訪問(wèn) Mvc 定義的路由 http://localhost:65125/api/test,返回信息如圖所示:
上面正常的返回就表示我們的 Ocelot 和 Mvc 同時(shí)工作了~
Reference
https://github.com/ThreeMammals/Ocelot
https://github.com/WeihanLi/AspNetCorePlayground/tree/master/TestGateway
總結(jié)
以上是生活随笔為你收集整理的让 Ocelot 与 asp.net core “共存”的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Web API 速率限制(二)- 令牌桶
- 下一篇: .NET Core IoT 入门指南:(