【.NET Core项目实战-统一认证平台】第三章 网关篇-数据库存储配置(1)
【.NET Core項目實戰(zhàn)-統(tǒng)一認證平臺】開篇及目錄索引
本篇將介紹如何擴展Ocelot中間件實現(xiàn)自定義網(wǎng)關(guān),并使用2種不同數(shù)據(jù)庫來演示Ocelot配置信息存儲和動態(tài)更新功能,內(nèi)容也是從實際設計出發(fā)來編寫我們自己的中間件,本文內(nèi)容涵蓋設計思想內(nèi)容和代碼內(nèi)容,我希望園友們最好跟著我這個文章的思路先理解好后再看源代碼,這樣有利于融會貫通,本篇的文檔及源碼將會在GitHub上開源,每篇的源代碼我將用分支的方式管理,本篇使用的分支為course1。
附文檔及源碼下載地址:[https://github.com/jinyancao/CtrAuthPlatform/tree/course1]
一、數(shù)據(jù)庫設計
上一篇中我們介紹了Ocelot中要滿足我們需求,我們需要把配置信息轉(zhuǎn)到數(shù)據(jù)庫存儲,今天我們就從數(shù)據(jù)庫設計開始,數(shù)據(jù)庫設計我采用的是PowerDesigner,首先打開軟件,新建一個概念模型。根據(jù)Ocelot的配置文件,我們可以發(fā)現(xiàn),配置信息由全局配置信息和路由信息組成,這時候我們可以設計表結(jié)構(gòu)如下,為了滿足后續(xù)多個路由的切換,增加了網(wǎng)關(guān)和路由多對多關(guān)系,以后我們可以隨時根據(jù)不同規(guī)則切換,詳細的表字段可以自行根據(jù)Ocelot配置文檔和設計文檔對照查看,這里我移除了限流的字段,因為我們后續(xù)需要自定義限流,用不上原來的方法。
生成物理模型
數(shù)據(jù)庫設計好后,我們需要把概念模型轉(zhuǎn)成物理模型,使用Ctrl+Shift+P快捷鍵,我們默認使用MSSQL2008R2實現(xiàn)配置存儲,所有在彈出的對話框中選擇,然后點擊確認后會自動生成MSSQL2008R2的物理模型,可以看到數(shù)據(jù)類型和表之間的關(guān)連關(guān)系都生成好了,奈斯,一切都是那么完美,如果主鍵為自增類型,手動標記下即可。
現(xiàn)在我們需要生成我們創(chuàng)建數(shù)據(jù)庫的SQL腳本了,別忘了保存下剛才生成的物理模型,因為以后還需要用到。
生成數(shù)據(jù)庫腳本
如圖所示,可以使用快捷鍵Ctrl+G生成數(shù)據(jù)庫腳本,點擊確認生成并保存,然后把生成的腳本在我們新建的數(shù)據(jù)庫里執(zhí)行,這樣我們的數(shù)據(jù)庫就設計完成了。
二、搭建并測試中間件
我們使用VS2017新建一個.NETCORE2.1項目,然后新建一個類庫來實現(xiàn)我們Ocelot定制版中間件,建好后項目結(jié)構(gòu)如下,現(xiàn)在開始我們第一個AhphOcelot定制中間件編寫。
首先我們回顧下【.NET Core項目實戰(zhàn)-統(tǒng)一認證平臺】第二章網(wǎng)關(guān)篇-重構(gòu)Ocelot來滿足需求的源碼解析,關(guān)于配置信息的讀取如下,我們只需要重寫下CreateConfiguration方法實現(xiàn)從數(shù)據(jù)庫里取就可以了,既然有思路了,
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) { //創(chuàng)建配置信息var configuration = await CreateConfiguration(builder);ConfigureDiagnosticListener(builder);return CreateOcelotPipeline(builder, pipelineConfiguration); }那就開始改造吧,我們新建一個Ctr.AhphOcelot類庫,來實現(xiàn)這個中間件,首先新建自定義中間件擴展,這個擴展是在原有的Ocelot的基礎上進行改造,所以需要先在Nuget中安裝Ocelot,這系列課程我們以最新的Ocelot 12.0.1版本進行擴展。
首先我們要了解,Ocelot的配置信息是怎么加載進來的呢?
private static async Task<IInternalConfiguration> CreateConfiguration(IApplicationBuilder builder) {// make configuration from file system?// earlier user needed to add ocelot files in startup configuration stuff, asp.net will map it to thisvar fileConfig = builder.ApplicationServices.GetService<IOptionsMonitor<FileConfiguration>>();// now create the configvar internalConfigCreator = builder.ApplicationServices.GetService<IInternalConfigurationCreator>();var internalConfig = await internalConfigCreator.Create(fileConfig.CurrentValue);//Configuration error, throw error messageif (internalConfig.IsError){ThrowToStopOcelotStarting(internalConfig);}// now save it in memoryvar internalConfigRepo = builder.ApplicationServices.GetService<IInternalConfigurationRepository>();internalConfigRepo.AddOrReplace(internalConfig.Data);fileConfig.OnChange(async (config) =>{var newInternalConfig = await internalConfigCreator.Create(config);internalConfigRepo.AddOrReplace(newInternalConfig.Data);});var adminPath = builder.ApplicationServices.GetService<IAdministrationPath>();var configurations = builder.ApplicationServices.GetServices<OcelotMiddlewareConfigurationDelegate>();// Todo - this has just been added for consul so far...will there be an ordering problem in the future? Should refactor all config into this pattern?foreach (var configuration in configurations){await configuration(builder);}if(AdministrationApiInUse(adminPath)){//We have to make sure the file config is set for the ocelot.env.json and ocelot.json so that if we pull it from the //admin api it works...boy this is getting a spit spags boll.var fileConfigSetter = builder.ApplicationServices.GetService<IFileConfigurationSetter>();await SetFileConfig(fileConfigSetter, fileConfig);}return GetOcelotConfigAndReturn(internalConfigRepo); }查看源碼后發(fā)現(xiàn)是是從OcelotBuilder加載的配置文件,也就是最早的AddOcelot()方法時注入的。
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot) {Configuration = configurationRoot;Services = services;//服務注冊,可以使用IOptions<FileConfiguration>調(diào)用Services.Configure<FileConfiguration>(configurationRoot);.... }現(xiàn)在我們要實現(xiàn)從數(shù)據(jù)庫提取配置信息,可以查看下Ocelot是否給我們提供了相關(guān)擴展接口,通過Ctrl+F查找FileConfiguration實體在哪些地方可以返回,IFileConfigurationRepository接口一眼就能認出,配置文件倉儲類,我們可以重寫這個接口實現(xiàn)即可完成配置文件從數(shù)據(jù)庫提取,果然Ocelot是為定制而生,其實如果沒有這個接口問題也不大,我們自己去定義和實現(xiàn)這個接口也一樣可以完成。
using System.Threading.Tasks; using Ocelot.Configuration.File; using Ocelot.Responses;namespace Ocelot.Configuration.Repository {public interface IFileConfigurationRepository{Task<Response<FileConfiguration>> Get();Task<Response> Set(FileConfiguration fileConfiguration);} }我們看看這個接口是否有默認實現(xiàn),DiskFileConfigurationRepository方法實現(xiàn)了這個接口,通過名稱就知道是直接從配置文件提取配置信息,再看下這個接口應用到哪里,繼續(xù)Ctrl+F找到,FileConfigurationPoller和FileAndInternalConfigurationSetter兩個地方用到了這個接口,其中FileConfigurationPoller實現(xiàn)了IHostedService后臺任務,我們不難看出,這個是一個定時更新任務,實際我們配置信息變更,肯定由管理員自己修改測試無誤后發(fā)起,這里我們用不上,但是實現(xiàn)思路可以了解下。FileAndInternalConfigurationSetter是配置文件更新方法,這里我們?nèi)绻褂脭?shù)據(jù)庫存儲,更新肯定由我們自己管理界面更新,所以也用不上,這時有人會問,那如果配置文件發(fā)生變更了,我們怎么去更新。這時候我們需要了解配置信息在哪里使用,是否使用了緩存。其實上面也給出了答案,就是IInternalConfiguration.
// now create the config var internalConfigCreator = builder.ApplicationServices.GetService<IInternalConfigurationCreator>(); var internalConfig = await internalConfigCreator.Create(fileConfig.CurrentValue);現(xiàn)在問題都梳理清楚了,現(xiàn)在我們實現(xiàn)的思路就是,首先通過數(shù)據(jù)庫實現(xiàn)IFileConfigurationRepository接口內(nèi)容(更新不需要實現(xiàn),前面說過了),然后再我們數(shù)據(jù)庫里修改了配置,更新IInternalConfiguration配置信息,即可完成我們的自定義任何地方的存儲。
開發(fā)的思路就是頂層開始一步一步往下實現(xiàn),最后完成我們的擴展。現(xiàn)在回到我們自己的代碼,修改配置信息代碼如下,是不是精簡很多了,但是有2個問題未解決,一是需要實現(xiàn)IFileConfigurationRepository,二是還沒實現(xiàn)動態(tài)更新。
private static async Task<IInternalConfiguration> CreateConfiguration(IApplicationBuilder builder) {//提取文件配置信息var fileConfig = await builder.ApplicationServices.GetService<IFileConfigurationRepository>().Get();var internalConfigCreator = builder.ApplicationServices.GetService<IInternalConfigurationCreator>();var internalConfig = await internalConfigCreator.Create(fileConfig.Data);//如果配置文件錯誤直接拋出異常if (internalConfig.IsError){ThrowToStopOcelotStarting(internalConfig);}//配置信息緩存,這塊需要注意實現(xiàn)方式,因為后期我們需要改造下滿足分布式架構(gòu),這篇不做講解var internalConfigRepo = builder.ApplicationServices.GetService<IInternalConfigurationRepository>();internalConfigRepo.AddOrReplace(internalConfig.Data);return GetOcelotConfigAndReturn(internalConfigRepo); }1、實現(xiàn)IFileConfigurationRepository接口
本系列所有課程都是基于輕量級的ORM框架dapper實現(xiàn)
首先需要NuGet包里添加Dapper,然后我們需要把設計的表生成實體,至于如何生成這里就不介紹了,實現(xiàn)方式很多,相關(guān)的帖子很多。使用Dapper時,我們需要知道知道連接方式,這時需要在中間件的基礎上擴充一個配置文件接收配置數(shù)據(jù),這樣我們才能使用配置的信息內(nèi)容。
namespace Ctr.AhphOcelot.Configuration {/// <summary>/// 金焰的世界/// 2018-11-11/// 自定義配置信息/// </summary>public class AhphOcelotConfiguration{/// <summary>/// 數(shù)據(jù)庫連接字符串/// </summary>public string DbConnectionStrings { get; set; }} }現(xiàn)在可以實現(xiàn)接口了,詳細代碼如下,代碼很簡單,就是從數(shù)據(jù)庫查詢出錄入的內(nèi)容,使用dapper實現(xiàn)。
using Ctr.AhphOcelot.Configuration; using Ctr.AhphOcelot.Model; using Dapper; using Ocelot.Configuration.File; using Ocelot.Configuration.Repository; using Ocelot.Responses; using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Text; using System.Threading.Tasks;namespace Ctr.AhphOcelot.DataBase.SqlServer {/// <summary>/// 金焰的世界/// 2018-11-11/// 使用SqlServer來實現(xiàn)配置文件倉儲接口/// </summary>public class SqlServerFileConfigurationRepository : IFileConfigurationRepository{private readonly AhphOcelotConfiguration _option;public SqlServerFileConfigurationRepository(AhphOcelotConfiguration option){_option = option;}/// <summary>/// 從數(shù)據(jù)庫中獲取配置信息/// </summary>/// <returns></returns>public async Task<Response<FileConfiguration>> Get(){#region 提取配置信息var file = new FileConfiguration();//提取默認啟用的路由配置信息string glbsql = "select * from AhphGlobalConfiguration where IsDefault=1 and InfoStatus=1";//提取全局配置信息using (var connection = new SqlConnection(_option.DbConnectionStrings)){var result = await connection.QueryFirstOrDefaultAsync<AhphGlobalConfiguration>(glbsql);if (result != null){var glb = new FileGlobalConfiguration();//賦值全局信息glb.BaseUrl = result.BaseUrl;glb.DownstreamScheme = result.DownstreamScheme;glb.RequestIdKey = result.RequestIdKey;glb.HttpHandlerOptions = result.HttpHandlerOptions?.ToObject<FileHttpHandlerOptions>();glb.LoadBalancerOptions = result.LoadBalancerOptions?.ToObject<FileLoadBalancerOptions>();glb.QoSOptions = result.QoSOptions?.ToObject<FileQoSOptions>();glb.ServiceDiscoveryProvider = result.ServiceDiscoveryProvider?.ToObject<FileServiceDiscoveryProvider>();file.GlobalConfiguration = glb;//提取所有路由信息string routesql = "select T2.* from AhphConfigReRoutes T1 inner join AhphReRoute T2 on T1.ReRouteId=T2.ReRouteId where AhphId=@AhphId and InfoStatus=1";var routeresult = (await connection.QueryAsync<AhphReRoute>(routesql, new { result.AhphId }))?.AsList();if (routeresult != null && routeresult.Count > 0){var reroutelist = new List<FileReRoute>();foreach (var model in routeresult){var m = new FileReRoute();m.AuthenticationOptions = model.AuthenticationOptions?.ToObject<FileAuthenticationOptions>();m.FileCacheOptions = model.CacheOptions?.ToObject<FileCacheOptions>();m.DelegatingHandlers = model.DelegatingHandlers?.ToObject<List<string>>();m.LoadBalancerOptions = model.LoadBalancerOptions?.ToObject<FileLoadBalancerOptions>();m.QoSOptions = model.QoSOptions?.ToObject<FileQoSOptions>();m.DownstreamHostAndPorts = model.DownstreamHostAndPorts?.ToObject<List<FileHostAndPort>>();//開始賦值m.DownstreamPathTemplate = model.DownstreamPathTemplate;m.DownstreamScheme = model.DownstreamScheme;m.Key = model.RequestIdKey;m.Priority = model.Priority ?? 0;m.RequestIdKey = model.RequestIdKey;m.ServiceName = model.ServiceName;m.UpstreamHost = model.UpstreamHost;m.UpstreamHttpMethod = model.UpstreamHttpMethod?.ToObject<List<string>>();m.UpstreamPathTemplate = model.UpstreamPathTemplate;reroutelist.Add(m);}file.ReRoutes = reroutelist;}}else{throw new Exception("未監(jiān)測到任何可用的配置信息");}}#endregionif (file.ReRoutes == null || file.ReRoutes.Count == 0){return new OkResponse<FileConfiguration>(null);}return new OkResponse<FileConfiguration>(file);}//由于數(shù)據(jù)庫存儲可不實現(xiàn)Set接口直接返回public async Task<Response> Set(FileConfiguration fileConfiguration){return new OkResponse();}} }現(xiàn)在又延伸出兩個問題.第一個是AhphOcelotConfiguration這個信息從哪讀取的?第二是SqlServerFileConfigurationRepository在哪注入。
其實讀過我前面中間件源碼解析的同學可能已經(jīng)知道了,就是在AddOcelot里注入的,現(xiàn)在我們就可以使用相同的方式實現(xiàn)自己的擴展。添加自己的ServiceCollectionExtensions擴展。
using Ctr.AhphOcelot.Configuration; using Ctr.AhphOcelot.DataBase.SqlServer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Ocelot.Configuration.Repository; using Ocelot.DependencyInjection; using System;namespace Ctr.AhphOcelot.Middleware {/// <summary>/// 金焰的世界/// 2018-11-11/// 擴展Ocelot實現(xiàn)的自定義的注入/// </summary>public static class ServiceCollectionExtensions{/// <summary>/// 添加默認的注入方式,所有需要傳入的參數(shù)都是用默認值/// </summary>/// <param name="builder"></param>/// <returns></returns>public static IOcelotBuilder AddAhphOcelot(this IOcelotBuilder builder, Action<AhphOcelotConfiguration> option){builder.Services.Configure(option);//配置信息builder.Services.AddSingleton(resolver => resolver.GetRequiredService<IOptions<AhphOcelotConfiguration>>().Value);//配置文件倉儲注入builder.Services.AddSingleton<IFileConfigurationRepository, SqlServerFileConfigurationRepository>();return builder;}} }有木有很簡單呢?到這里從數(shù)據(jù)庫中提取配置信息都完成啦,現(xiàn)在我們開始來測試下,看是否滿足了我們的需求。
新建一個Ctr.AuthPlatform.Gateway網(wǎng)關(guān)項目,添加我們的中間件項目引用,修改Startup.cs代碼如下
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Ocelot.DependencyInjection; using Ctr.AhphOcelot.Middleware; namespace Ctr.AuthPlatform.Gateway {public class Startup{public Startup(IConfiguration configuration){Configuration = configuration;}public IConfiguration Configuration { get; }public void ConfigureServices(IServiceCollection services){services.AddOcelot().AddAhphOcelot(option=>{option.DbConnectionStrings = "Server=.;Database=Ctr_AuthPlatform;User ID=sa;Password=bl123456;";});}public void Configure(IApplicationBuilder app, IHostingEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}else{app.UseExceptionHandler("/Error");}app.UseAhphOcelot().Wait();}} }就實現(xiàn)了自定義的網(wǎng)關(guān),是不是很優(yōu)雅呢?但是是否達到了我們預期的網(wǎng)關(guān)效果了,我們來直接從數(shù)據(jù)庫里插入測試數(shù)據(jù),并新建一個測試項目。測試數(shù)據(jù)腳本如下
--插入全局測試信息 insert into AhphGlobalConfiguration(GatewayName,RequestIdKey,IsDefault,InfoStatus) values('測試網(wǎng)關(guān)','test_gateway',1,1);--插入路由分類測試信息 insert into AhphReRoutesItem(ItemName,InfoStatus) values('測試分類',1);--插入路由測試信息 insert into AhphReRoute values(1,'/ctr/values','[ "GET" ]','','http','/api/Values','[{"Host": "localhost","Port": 9000 }]', '','','','','','','',0,1);--插入網(wǎng)關(guān)關(guān)聯(lián)表 insert into dbo.AhphConfigReRoutes values(1,1);測試項目結(jié)構(gòu)如下,就是默認的一個api項目,修改下啟動端口為9000。
為了方便調(diào)試.NETCORE項目,我建議使用dotnet run方式,分別啟動網(wǎng)關(guān)(7777端口)和測試服務(9999端口)。優(yōu)先啟動網(wǎng)關(guān)項目,想一想還有點小激動呢,開始運行項目,納尼,盡然報錯,而且是熟悉的未將對象引用到實例化錯誤,根據(jù)異常內(nèi)容可以看到是在驗證的時候報錯,我們可以查看下Ocelot對應的源代碼,發(fā)現(xiàn)問題所在了。
我們在一些未定義的配置項目使用了為空的賦值。而Ocleot里面對于不少配置項目未做非空驗證。比如RateLimitOptionsCreator對于FileGlobalConfiguration未做非空驗證,類似這樣的地方還有不少,我希望下次Ocelot更新時最好增加這類非空驗證,這樣便于自定義擴展,而Ocelot內(nèi)部實現(xiàn)了默認實例化,所以我們之前從數(shù)據(jù)庫取值賦值時寫法需要改進,修改后的代碼如下。
using Ctr.AhphOcelot.Configuration; using Ctr.AhphOcelot.Model; using Dapper; using Ocelot.Configuration.File; using Ocelot.Configuration.Repository; using Ocelot.Responses; using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Text; using System.Threading.Tasks;namespace Ctr.AhphOcelot.DataBase.SqlServer {/// <summary>/// 金焰的世界/// 2018-11-11/// 使用SqlServer來實現(xiàn)配置文件倉儲接口/// </summary>public class SqlServerFileConfigurationRepository : IFileConfigurationRepository{private readonly AhphOcelotConfiguration _option;public SqlServerFileConfigurationRepository(AhphOcelotConfiguration option){_option = option;}/// <summary>/// 從數(shù)據(jù)庫中獲取配置信息/// </summary>/// <returns></returns>public async Task<Response<FileConfiguration>> Get(){#region 提取配置信息var file = new FileConfiguration();//提取默認啟用的路由配置信息string glbsql = "select * from AhphGlobalConfiguration where IsDefault=1 and InfoStatus=1";//提取全局配置信息using (var connection = new SqlConnection(_option.DbConnectionStrings)){var result = await connection.QueryFirstOrDefaultAsync<AhphGlobalConfiguration>(glbsql);if (result != null){var glb = new FileGlobalConfiguration();//賦值全局信息glb.BaseUrl = result.BaseUrl;glb.DownstreamScheme = result.DownstreamScheme;glb.RequestIdKey = result.RequestIdKey;//glb.HttpHandlerOptions = result.HttpHandlerOptions?.ToObject<FileHttpHandlerOptions>();//glb.LoadBalancerOptions = result.LoadBalancerOptions?.ToObject<FileLoadBalancerOptions>();//glb.QoSOptions = result.QoSOptions?.ToObject<FileQoSOptions>();//glb.ServiceDiscoveryProvider = result.ServiceDiscoveryProvider?.ToObject<FileServiceDiscoveryProvider>();if (!String.IsNullOrEmpty(result.HttpHandlerOptions)){glb.HttpHandlerOptions = result.HttpHandlerOptions.ToObject<FileHttpHandlerOptions>();}if (!String.IsNullOrEmpty(result.LoadBalancerOptions)){glb.LoadBalancerOptions = result.LoadBalancerOptions.ToObject<FileLoadBalancerOptions>();}if (!String.IsNullOrEmpty(result.QoSOptions)){glb.QoSOptions = result.QoSOptions.ToObject<FileQoSOptions>();}if (!String.IsNullOrEmpty(result.ServiceDiscoveryProvider)){glb.ServiceDiscoveryProvider = result.ServiceDiscoveryProvider.ToObject<FileServiceDiscoveryProvider>();}file.GlobalConfiguration = glb;//提取所有路由信息string routesql = "select T2.* from AhphConfigReRoutes T1 inner join AhphReRoute T2 on T1.ReRouteId=T2.ReRouteId where AhphId=@AhphId and InfoStatus=1";var routeresult = (await connection.QueryAsync<AhphReRoute>(routesql, new { result.AhphId }))?.AsList();if (routeresult != null && routeresult.Count > 0){var reroutelist = new List<FileReRoute>();foreach (var model in routeresult){var m = new FileReRoute();//m.AuthenticationOptions = model.AuthenticationOptions?.ToObject<FileAuthenticationOptions>();//m.FileCacheOptions = model.CacheOptions?.ToObject<FileCacheOptions>();//m.DelegatingHandlers = model.DelegatingHandlers?.ToObject<List<string>>();//m.LoadBalancerOptions = model.LoadBalancerOptions?.ToObject<FileLoadBalancerOptions>();//m.QoSOptions = model.QoSOptions?.ToObject<FileQoSOptions>();//m.DownstreamHostAndPorts = model.DownstreamHostAndPorts?.ToObject<List<FileHostAndPort>>();if (!String.IsNullOrEmpty(model.AuthenticationOptions)){m.AuthenticationOptions = model.AuthenticationOptions.ToObject<FileAuthenticationOptions>();}if (!String.IsNullOrEmpty(model.CacheOptions)){m.FileCacheOptions = model.CacheOptions.ToObject<FileCacheOptions>();}if (!String.IsNullOrEmpty(model.DelegatingHandlers)){m.DelegatingHandlers = model.DelegatingHandlers.ToObject<List<string>>();}if (!String.IsNullOrEmpty(model.LoadBalancerOptions)){m.LoadBalancerOptions = model.LoadBalancerOptions.ToObject<FileLoadBalancerOptions>();}if (!String.IsNullOrEmpty(model.QoSOptions)){m.QoSOptions = model.QoSOptions.ToObject<FileQoSOptions>();}if (!String.IsNullOrEmpty(model.DownstreamHostAndPorts)){m.DownstreamHostAndPorts = model.DownstreamHostAndPorts.ToObject<List<FileHostAndPort>>();}//開始賦值m.DownstreamPathTemplate = model.DownstreamPathTemplate;m.DownstreamScheme = model.DownstreamScheme;m.Key = model.RequestIdKey;m.Priority = model.Priority ?? 0;m.RequestIdKey = model.RequestIdKey;m.ServiceName = model.ServiceName;m.UpstreamHost = model.UpstreamHost;m.UpstreamHttpMethod = model.UpstreamHttpMethod?.ToObject<List<string>>();m.UpstreamPathTemplate = model.UpstreamPathTemplate;reroutelist.Add(m);}file.ReRoutes = reroutelist;}}else{throw new Exception("未監(jiān)測到任何可用的配置信息");}}#endregionif (file.ReRoutes == null || file.ReRoutes.Count == 0){return new OkResponse<FileConfiguration>(null);}return new OkResponse<FileConfiguration>(file);}//由于數(shù)據(jù)庫存儲可不實現(xiàn)Set接口直接返回public async Task<Response> Set(FileConfiguration fileConfiguration){return new OkResponse();}} }然后重新運行,網(wǎng)關(guān)啟動成功。
接著我們啟動我們測試的服務,然后瀏覽器先訪問http://localhost:9000/api/values地址,測試地址正常訪問。
然后使用測試網(wǎng)關(guān)路由地址訪問http://localhost:7777/ctr/values,顯示內(nèi)容和本地訪問一樣,證明網(wǎng)關(guān)路由生效,是不是有點小激動呢?我們完成了從配置信息中取網(wǎng)關(guān)路由信息擴展。
三、下篇預告
最后我們回顧下這篇內(nèi)容,我是從設計到實現(xiàn)一步一步講解和實現(xiàn)的,而且實現(xiàn)過程是根據(jù)需求慢慢剖析再局部實現(xiàn)的,我發(fā)現(xiàn)現(xiàn)在很多人在平時學習基本都是結(jié)果未導向,很少去關(guān)心中間的實現(xiàn)過程,久而久之基本就會喪失解決問題的思路,寫的這么詳細,也是希望給大家一個解決問題的思路,目前我們實現(xiàn)了從數(shù)據(jù)庫中提取配置信息并在網(wǎng)關(guān)中生效,但是還未實現(xiàn)動態(tài)更新和擴展其他數(shù)據(jù)庫存儲,大家也可以先自己嘗試如何實現(xiàn)。
下一篇我們將會實現(xiàn)網(wǎng)關(guān)路由的動態(tài)更新,會提供幾種更新思路,根據(jù)實際情況擇優(yōu)選擇。然后在使用Mysql數(shù)據(jù)庫來存儲配置信息,并擴展此網(wǎng)關(guān)實現(xiàn)很優(yōu)雅的配置,為什么使用mysql擴展實現(xiàn)呢?因為.netcore已經(jīng)跨平臺啦,后期我們準備在Centos下實現(xiàn)容器化部署,這時我們就準備以mysql為例進行講解,本網(wǎng)關(guān)所有內(nèi)容源碼都會實現(xiàn)sqlserver和mysql兩種方式,其他存儲方式可自行擴展即可。
最后項目所有的文檔在源碼的文檔目錄,文檔按照課程源碼文件夾區(qū)分,本文的文檔標識course1。
我的博客即將同步至騰訊云+社區(qū),邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=l0q6lfr3asgg
posted on 2018-11-20 22:53 NET未來之路 閱讀(...) 評論(...) 編輯 收藏轉(zhuǎn)載于:https://www.cnblogs.com/lonelyxmas/p/9992353.html
總結(jié)
以上是生活随笔為你收集整理的【.NET Core项目实战-统一认证平台】第三章 网关篇-数据库存储配置(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 汇编实验报告(一)
- 下一篇: linux 安装 Elasticsear