我來給.Net設計一款HttpClient

一、前言

時間飛快,轉眼半年,碌碌無爲,眼下就要三十而立,回想三年前的今天,我將NetworkSocket庫開放到github,一直在更新與學習,不求有這個庫能有多好,只求本身在過程能成長,將領悟到一些思想應用到庫裏面去。今天,我來給你們介紹半年前在github開放的WebApiClient這個庫,正如NetworkSocket同樣,它正在漸漸從眇小變得強大,從簡單變得抽象、易用、可高度擴展,它將帶你進入不一個和以往徹底不一樣風格的調用http接口的世界。git

 

二、編程風格

2.1傳統調用風格

通常的,咱們須要new 一個HttpClient實例,而後準備請求url、請求body的HttpContent,而後發送,等待接收,解析回覆內容.....github

這些都是須要一行一行代碼來實現,代碼裏不只表現了「作什麼(What)」,並且更多表現出「如何(How)」完成工做這樣的實現細節,大概想一想代碼像下面:web

/// <summary>
/// 更新用戶信息
/// 使用application/json提交
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
async static Task<UserInfo> UpdateAsync(UserInfo user)
{
    var httpClient = new HttpClient();
    var serializer = new JavaScriptSerializer();

    var json = serializer.Serialize(user);
    var content = new StringContent(json, Encoding.UTF8, "application/json");
    var url = "http://localhost:9999/webapi/user/updateWithJson";

    var response = await httpClient.PostAsync(url, content);

    var resJson = await response.Content.ReadAsStringAsync();
    var resUser = serializer.Deserialize<UserInfo>(resJson);
    return resUser;
}
傳統What How思想代碼

服務端任何接口的小變化,都直接影響到咱們接口調用的某行具體代碼,若是有100個接口,這代碼的維護也不小。sql

2.2WebApiClient風格

WebApiClient的設計能夠解放使用者的勞動力,只須要使用者根據http接口來定義一份.Net的interface,在接口裏描述你想要什麼(what i wan),但不須要實現這份interface(how it do),大概以下:編程

[HttpHost("http://localhost:9999")]
public interface UserApi
{
    [HttpPost("/webapi/user/UpdateWithJson")]
    Task<UserInfo> UpdateWithJsonAsync([JsonContent] UserInfo user);
}

那麼,接口的實現者給誰來完成呢?給WebApiClient來完成,它很聰明,知道你想要什麼,這個正像咱們寫一條sql:select * from table同樣,只有what,沒有how。json

 

 

三、使用層設計

3.1接口與服務端設計一致

使用者編寫的interface,能夠與服務端接近徹底一致,在編寫接口或文檔對照方面至關容易。api

3.2使用Attribute標記描述「幹什麼」

上面的[HttpPost]和[JsonContent],用來標記是幹什麼(不是怎麼幹)緩存

3.3沒有了,只剩下調用接口了

var userApi = new HttpApiClient().Implement<UserApi>();
var resUser = await userApi.UpdateWithJsonAsync(user);

 

 

四、架構層設計

使用Castle來動態實現interface的實例,並得到實例方法調用的攔截,在攔截層,一一調用與方法相關標記的Atribute,Attribute是真正的邏輯實現者,每一個Attribute只關注本身應該作什麼。架構

4.一、ApiActionContext

ApiActionContext用於描述接口的詳細信息以及接口周邊的其它信息,在攔截interface的實例某方法以後,都生成一份ApiActionContext實例,但實例的不少屬性是緩存中獲取的,任何特性在執行的時候,均可以訪問和修改這個ApiActionContext。app

4.二、Attribute標記分類

一、IHttpActionAttribute (ApiActionContext)         // 與Api方法相關

二、IApiParameterAttribute (ApiActionContext)    // 與 api參數相關

三、IApiActionFilterAttribute (ApiActionContext)   // 與Api請求先後有關

四、IApiReturnAttribute (ApiActionContext)          // 與api返回值相關

這4個Attribute接口有着各自的職責,前三者一個共同的目標:構造和影響一個請求內容對象HttpRequestMessage,第4個的目標是:從回覆的HttpResponseMessage中獲得接口的返回值

4.3 Attribute的執行

在攔截器裏,按照IApiActionAttribute > IApiParameterAttribute > IApiActionFilterAttribute > IApiReturnAttribute的順序,將與方法的全部特性都執行,就能夠完成接口的調用,大概實現以下:

/// <summary>
/// 異步執行api
/// </summary>
/// <param name="context">上下文</param>
/// <returns></returns>
private async Task<object> ExecuteInternalAsync(ApiActionContext context)
{
    var apiAction = context.ApiActionDescriptor;

    foreach (var actionAttribute in apiAction.Attributes)
    {
        await actionAttribute.BeforeRequestAsync(context);
    }

    foreach (var parameter in apiAction.Parameters)
    {
        foreach (var parameterAttribute in parameter.Attributes)
        {
            await parameterAttribute.BeforeRequestAsync(context, parameter);
        }
    }

    foreach (var filter in apiAction.Filters)
    {
        await filter.OnBeginRequestAsync(context);
    }

    // 執行Http請求,獲取回覆對象
    var httpClient = context.HttpClientContext.HttpClient;
    context.ResponseMessage = await httpClient.SendAsync(context.RequestMessage);

    foreach (var filter in apiAction.Filters)
    {
        await filter.OnEndRequestAsync(context);
    }

    return await apiAction.Return.Attribute.GetTaskResult(context);
}

 

五、支持的功能特性

5.1方法或接口級特性

絕對主機域名:[HttpHost]

請求方式與路徑:[HttpGet]、[HttpPost]、[HttpDelete]、[HttpPut]、[HttpHead]和[HttpOptions]

代理:[Proxy]

請求頭:[Header]

返回值:[AutoReturn]、[JsonReturn]、[XmlReturn]

使用者能夠本身擴充更多特性。

5.2 參數級特性

路徑或query:[PathQuery]、[Url]

請求頭:[Header]

請求Body:[HttpContent]、[JsonContent]、[XmlContent]、[FormContent]、[MulitpartConten]

使用者能夠本身擴充更多特性。

5.3特殊參數類型

MulitpartFile類(表單文件)、Url類(請求地址)、Proxy類 (請求代理)

這些特殊參數類型在參數裏,能夠是本類型或本類型的集合,都會被執行

使用者能夠本身擴充更多的特殊參數類型。

 

六、擴展能力

6.1 擴展特性

任何只要實現了IHttpActionAttribute、IApiParameterAttribute 、IApiActionFilterAttribute 、IApiReturnAttribute 之一或以上的特性,只要打在接口或參數上,就會獲得調用,在調用裏實現處理邏輯。

6.2 特殊參數擴展

任何實現了IApiParameterable接口的參數值,也會獲得調用。

6.3 自定義xml/json序列化

HttpApiClient.Config.UseXmlFormatter(your formatter)

HttpApiClient.Config.UseJsonFormatter(your formatter)

6.4 自定義HttpClient上下文提供者

HttpApiClient.Config.UseHttpClientContextProvider(your provider)

你能夠本身控制HttpClient的配置與生命週期

6.5 自定義過濾器

繼承ApiActionFilterAttribute,能夠實現本身的攔截器,做日誌、受權什麼的均可以;

在子類修改AllowMultiple屬性與OrderIndex屬性,能夠實現特性的排序與是否在接口和方法上重複使用。

 

七、項目地址

https://github.com/xljiulang/WebApiClient

裏面有一個demo,藉助networksocket,http服務端與客戶端都是同一個進程,調試過程很是方便,數據流向一目瞭然。

最後,若是你看哪一個哪.net 的httpClient有更方便的調用,請@我,我立刻模仿他。

相關文章
相關標籤/搜索