ABP Vnext UI 原理 自動API 和C#動態api客戶端
自動Api控制器
當你從領域層以及數據持久層中查詢出了數據並且 將他們轉交給應用層進行顯示時 你發現 顯示之前經過HttpApi層的中轉 還要托管再Web項目中
1.手動api控制器
第一步 為什么要有這個Httpapi層 UI界面為了實現應用層接口的通用 如果將web何應用層直接依賴 第三方就沒有可用的直接webapi可以調用 你發現你還需要在寫一個webapi 所以我們應該創建一個專門的接口層讓web直接去托管他 這樣不僅web可以直接快速部署也可以供第三方直接訪問接口
第二步 在Httpapi層中創建控制器 依賴公共應用層 只依賴了接口 這時候你發現了 那么如果只依賴接口是肯定不行的 兩種辦法:插件加載實現類 以及 web托管(我們這次選擇這個 並用httpapi進行暴露接口在host托管項目中你可以看見依賴了實現)
我想強調一點的是 這其實是一種延遲的思想
為什么需要把業務上的控制器獨立出來成一個項目 而不是直接和web主機的控制器放在一起
業務角度: 原因就是為了做成模塊化 為了在所有模塊中進行復用 強調一點 模塊化就是為了復用 如果和主機放在一起 就不能復用 所以我們只能把他做成獨立的項目拆分出來
代碼角度:復用控制器 在abp框架中 通過Abp.ASP.NETCOREMVC 模塊復用 我們現在已知的httpapi的作用是 UI將商品給到hhtpapi 然后通過他委托給servers邏輯 在這里 我們是不是可以直接通過servers 就能生成api呢?通過他的名稱 生成 這樣我們就不用自己寫控制器了
第三步:自動api的生成:在應用實現層中 繼承 IRemoteService 接口 即可使用abp 自動api控制器 如果你不想讓某個服務暴露 你可以給某個服務上 加上 特性 [RemoteService(IsEnabled =false)] 默認為True 即可
第四步 解析 abp是如何 通過applicaitionserver 自動生成控制:在托管主機的模塊類中 有代碼
1 private void ConfigureConventionalControllers() 2 { 3 Configure<AbpAspNetCoreMvcOptions>(options => 4 { 5 options.ConventionalControllers.Create(typeof(EBusinessApplicationModule).Assembly, options => { 6 7 options.RootPath = "My.EBusiness"; 8 }); 9 }); 10 }
加載當前項目自動生成控制器
options.RootPath = "My.EBusiness"; 此選項可以規定api名稱

具體加載 依賴Application 通過自動api控制器加載方法 把servers 轉化為控制器 運行一下 紅框中就是自動生成的控制器 以及指定名稱

我們看源碼 究竟是如何生成的?
我們找到 Volo.Abp.Asp.NetCore.MVC--》Volo->Abp->ASPNETCORE->MVC->Conventions 在該模塊中 有生成api的各種約定
當你的servers繼承了 IRemoteService 接口時 他會將該類中 方法 以Get post Update delete 生成

Get Post Put Delet 方法 截取具體方法名 做成api的名稱 以及獲取相應的參數 /api/app/product 其中api不能更改 app可以通過上述 選項進行修改 后面具體方法名稱以及方法參數 則是abo自動解析
思考 :既然這樣能夠自動生成 為何我們還要寫一個Httpapit呢 明明可以自動生成 :
答案:為了能夠實現個性化的api 所以 abp在此 既考慮了 復用 又考慮到了 個性化 以及特性對servers 過濾加上 [RemoteService(IsEnabled =false)]
動態C#Api客戶端 abp核心模塊 之 HttpApi.Client
什么是 動態C#Api客戶端:對應用servers接口的動態代理 目的 解決客戶端調用服務端復雜性的問題;
上一節中 我們暴露的是webapi接口 如果我們使用webapi調用服務時 每增加一個需求 就會增加一個 類去調用服務 者極大的違背了 開閉原則
第一步 為什么要有HttpClient 如果我們只想讓我們的系統通過webapi 或者grpc/rpc進行遠程調用 就需要使用HttpClient了 也就是說 如果我們整個項目只需要做成webapi的形式 那我們只需要選擇一個就行了 並通過web托管 細心的同學可能想到 既然Httpapi 和Httpclient 都是可以做遠程通信為何要重復做一件事呢? 實際上在Httpapi.Client 是直接可以使用RPC直接進行遠程調用的 當然也可進行托管 而Httpapi則是 真正的Webapi項目( 頁面上接收參數有復用 一對多的關系)
如何使用C#動態api客戶端:創建一個 客戶端以調用 我們的服務
實現C#api動態客戶端的前提 1:OA項目 是個模塊項目 2:讓OA項目 依賴Httpapi.Client 並且在OA中引入Httpapi.Client
既然普通的Http調用違背了開閉原則 那我們該如何調用呢? 在Abp 的HttpApi.Client的加持下 使用IOC注入application.Contracts 的接口 直接進行具體的方法調用 就能完成調用
啟動測試 發現 接口變成了代理類而不是原來的接口 這個代理類就是 Httpclient創建的
C#動態api客戶端 原理分析:
原理可以總結一句話 當你調用接口時httpclient 攔截執並通過http形式執行你接口對應服務 可以通過日志輸出可以明顯看見 他替我們完成了最為繁瑣的一步
1.OA--依賴--》Httpapi.Client -----依賴 --》application.Contracts 這樣會讓OA間接依賴 application.Contracts 才能使用IOC進行獲取 但是 依賴的時接口 我們肯定不能直接調用;----------》動態代理
2.此時需要為接口創建代理:此時 abp 的HttpApi.Client基於application.Contracts 的接口 創建了代理(提前透露 所謂的代理 其實就是獲取服務名稱 然后拼接出真正的Url 使用HttpClient 去調用 獲取返回結果)--------》拼接URL 並且通過httpclient進行調用
源碼分析
首先動態代理的代碼在項目HttpApi.Client 模塊類中
其中
AddHttpClientProxies 就是創建代理的具體方法
public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddHttpClientProxies( typeof(EBusinessApplicationContractsModule).Assembly, RemoteServiceName ); }
查看源碼 FRAMEWORK-->SRC--->Volo.Abp.Http.clien-->Extensions--->Dependencyinjetion-->
1 public static IServiceCollection AddStaticHttpClientProxies( 2 [NotNull] this IServiceCollection services, 3 [NotNull] Assembly assembly, 4 [NotNull] string remoteServiceConfigurationName = RemoteServiceConfigurationDictionary.DefaultName) 5 { 6 Check.NotNull(services, nameof(assembly)); 7 8 var serviceTypes = assembly.GetTypes().Where(IsSuitableForClientProxying).ToArray(); 9 10 foreach (var serviceType in serviceTypes) 11 { 12 AddHttpClientFactory(services, remoteServiceConfigurationName); 13 14 services.Configure<AbpHttpClientOptions>(options => 15 { 16 options.HttpClientProxies[serviceType] = new HttpClientProxyConfig(serviceType, remoteServiceConfigurationName); 17 }); 18 } 19 20 return services; 21 }

1 public static IServiceCollection AddHttpClientProxy( 2 [NotNull] this IServiceCollection services, 3 [NotNull] Type type, 4 [NotNull] string remoteServiceConfigurationName = RemoteServiceConfigurationDictionary.DefaultName, 5 bool asDefaultService = true) 6 { 7 Check.NotNull(services, nameof(services)); 8 Check.NotNull(type, nameof(type)); 9 Check.NotNullOrWhiteSpace(remoteServiceConfigurationName, nameof(remoteServiceConfigurationName)); 10 11 AddHttpClientFactory(services, remoteServiceConfigurationName); 12 13 services.Configure<AbpHttpClientOptions>(options => 14 { 15 options.HttpClientProxies[type] = new HttpClientProxyConfig(type, remoteServiceConfigurationName); 16 }); 17 18 var interceptorType = typeof(DynamicHttpProxyInterceptor<>).MakeGenericType(type); 19 services.AddTransient(interceptorType); 20 21 var interceptorAdapterType = typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(interceptorType); 22 23 var validationInterceptorAdapterType = 24 typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(typeof(ValidationInterceptor)); 25 26 if (asDefaultService) 27 { 28 services.AddTransient( 29 type, 30 serviceProvider => ProxyGeneratorInstance 31 .CreateInterfaceProxyWithoutTarget( 32 type, 33 (IInterceptor)serviceProvider.GetRequiredService(validationInterceptorAdapterType), 34 (IInterceptor)serviceProvider.GetRequiredService(interceptorAdapterType) 35 ) 36 ); 37 } 38 39 services.AddTransient( 40 typeof(IHttpClientProxy<>).MakeGenericType(type), 41 serviceProvider => 42 { 43 var service = ProxyGeneratorInstance 44 .CreateInterfaceProxyWithoutTarget( 45 type, 46 (IInterceptor)serviceProvider.GetRequiredService(validationInterceptorAdapterType), 47 (IInterceptor)serviceProvider.GetRequiredService(interceptorAdapterType) 48 ); 49 50 return Activator.CreateInstance( 51 typeof(HttpClientProxy<>).MakeGenericType(type), 52 service 53 ); 54 }); 55 56 return services; 57 }
AddHttpClientProxy
你會發現 這個方法 就做了一件事
context.Services.AddHttpClientProxies( typeof(EBusinessApplicationContractsModule).Assembly, RemoteServiceName
從
EBusinessApplicationContractsModule 此程序集中加載所有類型 通過for循環 調用AddHttpClientProxy方法 創建所有的代理 具體依賴來自於
Volo.abp.castle.core此模塊將在后期文章中詳解
這樣我們可以想一想 C#Api動態客戶端有什么缺陷?
不能跨語言 僅僅局限於ABp框架中 甚至是.NET5中
這就有了自動api了
動態客戶端對內調用 自動api向外提供訪問
三五八團楚雲飛的小Tips 模塊為了復用:業務模塊組件之 數據庫持久層
如何再數據持久層中切換數據庫 首先你可以查看源碼 abp封裝了MIc 的EntitiyFramework框架
再AbpEntitiyFramework中 我們默認使用的是Mysql 如何切換到Sqlserver 只需要下載組件 然后再數據持久層
修改 Options.UseSqlserver();即可