组件开始设计是针对以接口的方式来定义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-urlencoded
public class HttpFormUrlClient : HttpClient<FormUrlFormater>
{
public HttpFormUrlClient(string host) : base(host)
{
}
}
//用于json请求响应,对应application/json
public 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();
}
}
以上是组件的一些用例应用代码。