ABP vNext系列文章04---DynamicClient動態代理

一、動態代理在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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章