一、動態代理在ABP系統中的應用
1、它主要在做什麼事件
之前開發系統想要在後臺調用別的服務都是用HttpClient發起請求,在abp vnext中不需要我們這樣做了,
你只要知道服務調用的接口方法,就像調用本地代碼一樣調用接口,abp會動態生成一個http請求
這樣,我們就引出了一個概念叫:動態 C# API 客戶端
定義:ABP可以自動創建C# API 客戶端代理來調用遠程HTTP服務(REST APIS).通過這種方式,你不需要通過 HttpClient
或者其他低級的HTTP功能調用遠程服務並獲取數據.
2、如何申明一個遠程的api接口
定義一個接口,只有一個條件:必須實現IRemoteService
接口:
public interface IBookAppService : IApplicationService { Task<List<BookDto>> GetListAsync(); }
爲了能自動被發現,你的接口需要實現IRemoteService
接口.由於IApplicationService
繼承自IRemoteService
接口.所以IBookAppService
完全滿足這個條件.
在你的服務中實現這個類,你可以使用auto API controller system將你的服務暴漏爲一個REST API 端點.
3、添加 Volo.Abp.Http.Client nuget包
Install-Package Volo.Abp.Http.Client
增加的層爲 HttpApi.Client
[DependsOn(typeof(AbpHttpClientModule))] //添加依賴 public class MyClientAppModule : AbpModule { }
4、創建客戶端代理了.
例如:
[DependsOn( typeof(AbpHttpClientModule), //用來創建客戶端代理 typeof(MyApplicationContractsModule) //包含應用服務接口 )] public class MyClientAppModule : AbpModule {
private const string ProductRemoteServiceName = "ProductService"; public override void ConfigureServices(ServiceConfigurationContext context) { //創建動態客戶端代理 context.Services.AddHttpClientProxies( typeof(MyApplicationContractsModule).Assembly,ProductRemoteServiceName ); } }
AddHttpClientproxies
方法獲得一個程序集,找到這個程序集中所有的服務接口,創建並註冊代理類.我們所有要調用的接口都是程序集 MyApplicationContractsModule 中的。
5、修改配置文件
appsettings.json
文件中的RemoteServices
節點被用來設置默認的服務地址.下面是最簡單的配置:
{ "RemoteServices": { "ProductService": { "BaseUrl": "http://localhost:8012/", "UseCurrentAccessToken": "true" }, } }
當設置 UseCurrentAccessToken 爲true時說明請求是可以傳送token的。那此時還要引用一個nuget包:Volo.Abp.Http.Client.IdentityModel,注意是要xxx.Host項目下引用,而不是xxx.HttpApi.Client層
並增加依賴:[DependsOn(typeof(AbpHttpClientIdentityModelModule))]
6、如何使用
可以直接在你的類中使用,就像調用本地接口一樣,IBookAppService就是遠程要調用的服務
public class MyService : ITransientDependency { private readonly IBookAppService _bookService; public MyService(IBookAppService bookService) { _bookService = bookService; } public async Task DoIt() { var books = await _bookService.GetListAsync(); foreach (var book in books) { Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}"); } } }
二、源碼解析
在第一部分6節中,在使用注入接口IBookAppService時,在調用時其實是爲我們生成一個代理接口去訪問遠程的api,也就是說當你調用接口時httpclient 攔截執並通過http形式執行你接口對應服務
我們找到和系統集成的地方,在項目HttpApi.Client 模塊類中:
那我們就從這裏開始源碼的分析吧。此段代碼是將模塊中所有程序集和遠程的地址傳了進去,進入這個擴展方法AddHttpClientProxies,
擴展方法寫在類ServiceCollectionHttpClientProxyExtensions中:
其中代碼段:var serviceTypes = assembly.GetTypes().Where(IsSuitableForClientProxying).ToArray();就是從程序集中取出所有的符合要求的類,符合什麼要求呢,我們再看看IsSuitableForClientProxying(ps:沒想到方法在where中還可以這麼調用,get了)
在這個方法中我們看到了一個非常親切的接口IRemoteService,這就是我們自動api爲什麼要實現這個接口的原因了。
拿到我們所有符合要求的類後,通過foreach遍歷,將符合要求的類,遠程連接的地址繼續往下傳,調用方法AddHttpClientProxy,我們進這個方法看看做了些什麼
可以看到,主要是將我們的接口,動態的去創建了一個實實,然後注入到容器中。
其中有一條經常用到的代碼:typeof(AbpAsyncDeterminationInterceptor<>).MakeGenericType(interceptorType);
作用是爲泛型類指定泛型類型,也就是原來是AbpAsyncDeterminationInterceptor<T>,泛型T不明確,現在明確了爲 interceptorType
有一段非常重要也是核心的代碼:
該方法主要是給我們的接口類Type(例如:IBookAppService ),動態的創建了一個實現類,並注入到ioc中,這樣,我們在其它類中注入時,就有了該實例。
動態創建類是在第三方庫中實現的,比較複雜,暫不研究,有興趣可以參考開源:Https://github.com/castleproject/Core