我來給.Net設計一款HttpClient


1、前言

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

 

2、編程風格

2.1傳統調用風格

一般的,我們需要new 一個HttpClient實例,然后准備請求url、請求body的HttpContent,然后發送,等待接收,解析回復內容.....

這些都是需要一行一行代碼來實現,代碼里不僅表現了“做什么(What)”,而且更多表現出“如何(How)”完成工作這樣的實現細節,大概想想代碼像下面:

/// <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個接口,這代碼的維護也不小。

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。

 

 

3、使用層設計

3.1接口與服務端設計一致

使用者編寫的interface,可以與服務端接近完全一致,在編寫接口或文檔對照方面相當容易。

3.2使用Attribute標記描述“干什么”

上面的[HttpPost]和[JsonContent],用來標記是干什么(不是怎么干)

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

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

 

 

4、架構層設計

使用Castle來動態實現interface的實例,並獲得實例方法調用的攔截,在攔截層,一一調用與方法相關標記的Atribute,Attribute是真正的邏輯實現者,每個Attribute只關注自己應該做什么。

4.1、ApiActionContext

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

4.2、Attribute標記分類

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

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

3、IApiActionFilterAttribute (ApiActionContext)   // 與Api請求前后有關

4、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、支持的功能特性

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、擴展能力

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屬性,可以實現特性的排序與是否在接口和方法上重復使用。

 

7、項目地址

https://github.com/xljiulang/WebApiClient

里面有一個demo,借助networksocket,http服務端與客戶端都是同一個進程,調試過程非常方便,數據流向一目了然。

最后,如果你看哪個哪.net 的httpClient有更方便的調用,請@我,我馬上模仿他。

 


免責聲明!

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



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