Asp.NetCore abp vnext 自動api 和動態api客戶端


 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();即可

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM