组件开始设计是针对以接口的方式来定义HTTP/HTTPS访问,虽然基于接口来操作有很大的便利性,但定义起来就比较麻烦了。所以在1.5版本中实现了一个HttpClient类来简化调用。
HttpClient
该类支持HTTP的GET,POST,DELETE和PUT操作,通过这几个方法可以调用HTTP请求,包括application/json和上传文件等。
public class HttpClient<T>where T : IBodyFormater, new(){public HttpClient(string host){mHost = HttpHost.GetHttpHost(host);}private HttpHost mHost;private Dictionary<string, string> mQueryString = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);private Dictionary<string, string> mHeader = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);private object mDataObject;private Dictionary<string, object> mDataMap = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);public HttpClient<T> Accept(string value){mHeader["accept"] = value;return this;}public HttpClient<T> Authorization(string value){mHeader["authorization"] = value;return this;}public HttpClient<T> SetHeader(string name, string value){mHeader[name] = value;return this;}public HttpClient<T> AddQueryString(string name, object value){mQueryString[name] = value.ToString();return this;}public HttpClient<T> SetBody(object data){mDataObject = data;return this;}public HttpClient<T> AddBodyFile(string name, string file){AddBodyField(name, new FileInfo(file));return this;}public HttpClient<T> AddBodyFile(string name, UploadFile file){AddBodyField(name, file);return this;}public HttpClient<T> AddBodyField(string name, object data){mDataMap[name] = data;return this;}public async Task<RESULT> Get<RESULT>(string url){var response = await Get(url, typeof(RESULT));return response.GetResult<RESULT>();}public Task<Response> Get(string url, Type bodyType = null){var request = mHost.Get(url, mHeader, mQueryString, new T(), bodyType);return request.Execute();}public async Task<RESULT> Post<RESULT>(string url){var response = await Post(url, typeof(RESULT));return response.GetResult<RESULT>();}public Task<Response> Post(string url, Type bodyType = null){var request = mHost.Post(url, mHeader, mQueryString, mDataObject == null ? mDataMap : mDataObject, new T(), bodyType);return request.Execute();}public async Task<RESULT> Put<RESULT>(string url){var response = await Put(url, typeof(RESULT));return response.GetResult<RESULT>();}public Task<Response> Put(string url, Type bodyType = null){var request = mHost.Put(url, mHeader, mQueryString, mDataObject == null ? mDataMap : mDataObject, new T(), bodyType);return request.Execute();}public async Task<RESULT> Delete<RESULT>(string url){var response = await Delete(url, typeof(RESULT));return response.GetResult<RESULT>();}public Task<Response> Delete(string url, Type bodyType = null){var request = mHost.Delete(url, mHeader, mQueryString, new T(), bodyType);return request.Execute();}}
以上是类的完全整代码实现,代码量比较少归功于组件在基础上的基础封装。为了更方便使用组件在这基础上扩展了几种常用格式调用Client类。
//二进制流处理,下载文件public class HttpBinaryClient : HttpClient<BinaryFormater>{public HttpBinaryClient(string host) : base(host){}}//常用的Form url encoding编码,对应application/x-www-form-urlencodedpublic class HttpFormUrlClient : HttpClient<FormUrlFormater>{public HttpFormUrlClient(string host) : base(host){}}//用于json请求响应,对应application/jsonpublic class HttpJsonClient : HttpClient<JsonFormater>{public HttpJsonClient(string host) : base(host){}}//等价于multipart/form-data,常用于上传文件public class HttpFormDataClient : HttpClient<FromDataFormater>{public HttpFormDataClient(string host) : base(host){}}
可以根据自己需要来使用不同的Client。
自定义Formater
有很多时候请求和响应的内容不一致,这个时候就要用到自定义Formater了,组件支持这样的扩展,只需要FormaterAttribute对象重写相关方法即可。以下是BinaryFormater的扩展:
public class BinaryFormater : FormaterAttribute{public override string ContentType => "application/octet-stream";public override object Deserialization(Response response, PipeStream stream, Type type, int length){var result = System.Buffers.ArrayPool<byte>.Shared.Rent(length);stream.Read(result, 0, length);return new ArraySegment<Byte>(result, 0, length);}public override void Serialization(Request request, object data, PipeStream stream){if (data is Byte[] buffer){stream.Write(buffer, 0, buffer.Length);}else if (data is ArraySegment<byte> array){stream.Write(array.Array, array.Offset, array.Count);}else{throw new Exception("Commit data must be byte[] or ArraySegment<byte>");}}}
为了方便也可以继承已经实现的,重写单个方法。
使用
在使用之前需要引用BeetleX.Http.Clients,引用后即可使用组件来访问HTTP/HTTPS服务。
[Fact]public async Task HttpBin_Delete(){HttpJsonClient client = new HttpJsonClient("http://httpbin.org");var result = await client.Delete("/delete");Assert.Equal(null, result.Exception);}[Fact]public async Task HttpBin_Get(){HttpJsonClient client = new HttpJsonClient("http://httpbin.org");var result = await client.Get("/get");Assert.Equal(null, result.Exception);}[Fact]public async Task HttpBin_Post(){HttpJsonClient client = new HttpJsonClient("http://httpbin.org");var date = DateTime.Now;client.SetBody(date);var result = await client.Post("/post");JToken rdata = result.GetResult<JToken>()["data"];}[Fact]public async Task HttpBin_Put(){HttpJsonClient client = new HttpJsonClient("http://httpbin.org");Employee emp = DataHelper.Defalut.Employees[0];client.SetBody(emp);var result = await client.Post("/post");JToken rdata = result.GetResult<JToken>()["data"];}[Fact]public async Task GetImage(){HttpClient<BinaryFormater> client = new HttpClient<BinaryFormater>("http://httpbin.org");var result = await client.Get("/image");var data = result.GetResult<ArraySegment<byte>>();using (System.IO.Stream write = System.IO.File.Create("test.jpg")){write.Write(data.Array, data.Offset, data.Count);write.Flush();}}
以上是组件的一些用例应用代码。