庫簡介
WebApiClient是開源在github上的一個httpClient客戶端庫,內部基於HttpClient開發,是一個只需要定義c#接口(interface),並打上相關特性,即可異步調用http-api的框架 ,支持.net framework4.5+、netcoreapp2.0和netstandard2.0。本文將詳細地講解如何使用WebApiClient進行http接口的調用,給.net開發者提供一種有別於傳統的http接口調用編程方式。
1. 設計一個Get請求接口
1.1 最簡單的Get請求
public interface MyWebApi : IDisposable
{
// GET http://www.mywebapi.com/webapi/user?account=laojiu
[HttpGet("http://www.mywebapi.com/webapi/user")]
ITask<string> GetUserByAccountAsync(string account);
}
var myWebApi = HttpApiClient.Create<MyWebApi>();
var userStr = await myWebApi.GetUserByAccountAsync("laojiu");
myWebApi.Dispose();
1.2 使用[HttpHost]特性
如果你有多個接口,而且都指向對一服務器,可以將請求的域名抽出來放到HttpHost特性,接口的代碼如下:
[HttpHost("http://www.mywebapi.com")]
public interface MyWebApi : IDisposable
{
// GET /webapi/user?account=laojiu
[HttpGet("/webapi/user")]
ITask<string> GetUserByAccountAsync(string account);
}
1.3 使用強類型返回值處理xml或json
如果接口的返回內容是xml或json,你希望將它自動映射為強類型的模型,需要給接口打上對應的[XmlReturn]或[JsonResult]。實際上有一個[AutoReturn],它會根據回復頭標識,自動選擇不同的轉換器轉換為TResult類型的結果,默認的,每個接口都使用了[AutoReturn],除非給接口顯性地給方法配置了[XmlReturn]或[JsonResult]。如果以上返回的userStr是UserInfo類型的xml或json文本,那么強類型的代碼聲明如下:
[HttpHost("http://www.mywebapi.com")]
public interface MyWebApi : IDisposable
{
// GET /webapi/user?account=laojiu
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserByAccountAsync(string account);
}
[HttpHost("http://www.mywebapi.com")]
[JsonReturn] // 指明使用Json處理返回值為UserInfo類型
public interface MyWebApi : IDisposable
{
// GET /webapi/user?account=laojiu
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserByAccountAsync(string account);
}
2.請求URL的多個參數
2.1 參數平鋪
你可以將多個參數一一設計為接口的參數,類似於:
// GET /webapi/user?account=laojiu&password=123456
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserAsync(string account, string password);
2.2 參數合並到模型
也可以將所有參數合到一個簡單的多屬性模型對象:
public class MyParameters
{
public string Account { get; set; }
public string Password { get; set; }
}
// GET /webapi/user?account=laojiu&password=123456
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserAsync(MyParameters parameters);
2.3 模型+簡單參數混合
在一些場景中,除了提交多屬性模型對象之外,可能還需要一個簡單類型的額外參數,你可以如下編寫接口:
// GET /webapi/user?account=laojiu&password=123456&birthDay=2010-01-01 01:01:01
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserAsync(MyParameters parameters,DateTime birthDay);
上面這里,你可能會遇到一個問題,birthDay會簡單的ToString()值做為參數值,如果你希望只需要日期而不包含時間,你可以給birthDay指定格式:
// GET /webapi/user?account=laojiu&password=123456&birthDay=2010-01-01
[HttpGet("/webapi/user")]
ITask<UserInfo> GetUserAsync(
MyParameters parameters,
[PathQuery("yyyy-MM-dd")] DateTime birthDay);
實際上,對於沒有任何特性修飾的每個參數,都默認被[PathQuery]修飾,表示做為請求路徑或請求參數處理,[PathQuery]的構造器重載方法可以指定日期時間格式。
3.設計一個Post請求接口
3.1使用x-www-form-urlencoded提交請求
// POST webapi/user
// Body Account=laojiu&Password=123456
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync([FormContent] UserInfo user);
設計風格和Get請求是差不多的,你應該發現,接口參數被[FormContent]修飾了,[FormContent]的作用是將模型參數user以key1=value1&key2=value2的方式寫入到請求內容中。如果你還需要提供一個額外的簡單類型參數,需要使用[FormField]修飾這個參數,可以這樣設計接口:
// POST webapi/user
// Body Account=laojiu&Password=123456&fieldX=xxx
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync(
[FormContent] UserInfo user,
[FormField] string fieldX);
3.2使用multipart/form-data提交請求
// POST webapi/user
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithMulitpartAsync([MulitpartContent] UserInfo user);
// POST webapi/user
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithMulitpartAsync(
[MulitpartContent] UserInfo user,
[MulitpartText] string nickName,
MulitpartFile file);
需要了解的是,[MulitpartText]表示是一個文本項,而MulitpartFile表示一個文件項,MulitpartFile實現了IApiParameterable接口,它不需要任何特性的修飾,它能提供自我解釋和處理。
3.3提交Json或Xml文本
對於json和xml,只能一次性提交一個參數,不支持額外參數之說
// POST webapi/user
// Body user的json文本
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithJsonAsync([JsonContent] UserInfo user);
// POST webapi/user
// Body user的xml文本
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithXmlAsync([XmlContent] UserInfo user);
如果你的UserInfo有DateTime類型的屬性,你可以使用[JsonContent("時間格式")]來修飾接口參數,否則時間格式使用HttpApiConfig的DateTimeFormate。
3.4 提交原始的HttpContent
// POST webapi/user
// Body Account=laojiu&Password=123456
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync(FormUrlEncodedContent user);
// POST webapi/user
// Body Account=laojiu&Password=123456&age=18
[HttpPost("/webapi/user")]
ITask<UserInfo> UpdateUserWithFormAsync(
[HttpContent] FormUrlEncodedContent user,
[FormField] int age);
默認的,所有System.Net.Http.HttpContent類型的參數,都會被[HttpContent]特性修飾,而且可以與表單字段特性等混合使用。值得說明的話,傳統的System.Net.Http.HttpContent類型參數必須放到其它表單字段參數的前面。
4. 動態指定請求的域名或Url
4.1 域名動態而相對路徑固定
以上的例子,請求的根路徑都是硬編碼,而在不少場景中是放在配置文件中的,可以在創建接口實例時創建配置項:
var config = new HttpApiConfig
{
// 請求的域名,會覆蓋[HttpHost]特性
HttpHost = new Uri("http://www.webapiclient.com"),
};
var myWebApi = HttpApiClient.Create<MyWebApi>(config);
var userStr = await myWebApi.GetUserByAccountAsync("laojiu");
myWebApi.Dispose();
4.2 每個請求接口的URL路徑都是動態的
有時,多個接口方法的全部URL都是運行時才確定的,這時,需要給每個接口做如下的調整,注意[Url]特性表示參數是請求的URL,要求必須放在第一個參數。:
public interface MyWebApi : IDisposable
{
// GET {URL}?account=laojiu
[HttpGet]
ITask<string> GetUserByAccountAsync([Url] string url, string account);
}
4.3 相對路徑某個分段動態
有時,有些接口會將某個參數做路徑的一個分段,比如GET http://www.webapiclient.com/{account},這里的{account}是動態的,獲取哪個賬號的資料就填寫哪個賬號,可以如下設計接口
public interface MyWebApi : IDisposable
{
// GET http://www.webapiclient.com/laojiu
[HttpGet("http://www.webapiclient.com/{account}"]
ITask<string> GetUserByAccountAsync(string account);
}
5.參數別名或屬性別名
5.1 參數別名
有些服務端接口要求的鍵名與你的編程風格不一致,或者使用了特殊的鍵名為.net語言不允許的參數名,你可以使用[AliasAs("name")]來給參數或模型的屬性別名。
public interface MyWebApi : IDisposable
{
// GET http://www.mywebapi.com/webapi/user?_name=laojiu
[HttpGet("http://www.mywebapi.com/webapi/user")]
ITask<string> GetUserByAccountAsync(
[AliasAs("_name")] string account);
}
5.2 模型的屬性別名
public class UserInfo
{
[AliasAs("loginAccount")]
public string Account { get; set; }
public string Password { get; set; }
}
6.特性的范圍和優先級
6.1 特性的范圍
有些特性比如[Header],可以修飾於接口、方法和參數,使用不同的構造器和修飾於不同的地方產生的含義和結果是有點差別的:
- 修飾接口時,表示接口下的所有方法在請求前都會添加這個請求頭;
- 修飾方法時,表示此方法在語法前添加這個請求頭;
- 修飾參數時,表示參數的值將做為請求頭的值,由調用者動態傳入;
6.2 特性的優先級
有些特性比如[AutoReturn]和[JsonReturn],可以修飾於接口和方法,但特性的AllowMultiple為false,如果在接口級生明方法級[AutoReturn],在方法級上聲明[JsonReturn],此方法實際生效的是[JsonReturn];再比如[Timeout]特性,如果在接口級聲明[Timeout(5000)]在方法級聲明[Timeout(10000)],實際生效的是[Timeout(10000)],總結如下:
- AllowMultiple為false的同一個特性,方法級比接口級優先級高
- AllowMultiple為false的不同類型的[ReturnAttribute],方法級比接口級優先級高
