.NET C# 使用System.Net.Http下HttpClient执行异步网络数据传输任务
1. .NET C#可以使用的网络传输方法类库有很多,比如:System.Net.Http.HttpClient、System.Net.WebClient、System.Net.HttpWebRequest和System.Net.HttpWebResponse。
相关官方文档链接:
https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-5.0
https://docs.microsoft.com/zh-cn/dotnet/api/system.net.webclient?redirectedfrom=MSDN&view=net-5.0
https://docs.microsoft.com/zh-cn/dotnet/api/system.net.httpwebrequest?view=net-5.0
https://docs.microsoft.com/zh-cn/dotnet/api/system.net.httpwebresponse?view=net-5.0
现在微软官方比较推荐的是使用System.Net.Http.HttpClient来执行网络传输任务。
2. httpClient异步网络请求工具方法 示例代码如下:
public static class HttpClientExtensions
{
public static Task<HttpResponseMessage> PatchAsync(this HttpClient client, Uri uri, HttpContent content)
{
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, uri)
{
Content = content
};
return client.SendAsync(request);
}
}
public static class ApiHttpUtils
{
private static HttpClient _client;
static ApiHttpUtils()
{
//TODO 后续调整为先读取缓存,没有则新建之
_client = new HttpClient();
}
/// <summary>
/// 执行http的请求
/// </summary>
/// <param name="uri">执行的uri</param>
/// <param name="methodType">http方法类型</param>
/// <param name="bodyParam">POST\PUT\Patch方式时,传值参数</param>
/// <param name="inputMediaType">输入格式,即header里面的Content-Type的内容</param>
/// <param name="outputMediaType">输出格式,即header里面的Accept的内容</param>
/// <returns>执行结果</returns>
public static async Task<string> ExecuteHttpClientAsync(
Uri uri,
Enums.HttpMethodEnum methodType,
string bodyParam,
string inputMediaType = "json",
string outputMediaType = "json")
{
HttpResponseMessage result = null;
try
{
HttpClient client = CreateHttpClient(outputMediaType);
HttpContent content = null;
if (!string.IsNullOrWhiteSpace(bodyParam))
content = new StringContent(bodyParam, Encoding.UTF8, $"application/{inputMediaType}");
switch (methodType)
{
case Enums.HttpMethodEnum.Post:
result = await client.PostAsync(uri, content);
break;
case Enums.HttpMethodEnum.Put:
result = await client.PutAsync(uri, content);
break;
case Enums.HttpMethodEnum.Get:
result = await client.GetAsync(uri);
break;
case Enums.HttpMethodEnum.Delete:
result = await client.DeleteAsync(uri);
break;
case Enums.HttpMethodEnum.Patch:
result = await client.PatchAsync(uri, content);
break;
}
}
catch (AggregateException ex)
{
#region 获取线程异常信息
Exception firstException = null;
if (ex.InnerExceptions != null && ex.InnerExceptions.Any())
{
firstException = ex.InnerExceptions.First();
if (firstException.InnerException != null)
firstException = firstException.InnerException;
}
//Logger.LogException(firstException);//Nlog日志记录
#endregion 获取线程异常信息
result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content =
new StringContent(firstException != null
? firstException.ToString()
: "Encountered an AggreggateException without any inner exceptions")
};
}
catch (Exception ex)
{
//Logger.LogException(ex);//Nlog日志记录
result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content =
new StringContent("Encountered an AggreggateException without any inner exceptions")
};
}
return await result.Content.ReadAsStringAsync();
}
/// <summary>
/// 创建http的客户端
/// </summary>
/// <param name="outputMediaType">输出格式,即header里面的Accept的内容</param>
public static HttpClient CreateHttpClient(string outputMediaType = "json")
{
HttpClient client = _client;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue($"application/{outputMediaType}"));
return client;
}
}
3.Client调用方式如下,以WinForm窗体程序举例,某按钮点击事件后台方法:
public class InspectContentExtractReturnInputModel
{
public List<string> Datas { get; set; }
}
public static class SystemConfig
{
/// <summary>
/// 测试调用异步耗时webAPI不等待其返回值
/// </summary>
public static string TestTimeConsumWebApiUrl { get; set; }
public static void LoadSettings()
{
try
{
TestTimeConsumWebApiUrl = ConfigUtils.GetAppSetting("TestTimeConsumWebApiUrl");
}
catch (Exception ex)
{
}
}
}
public enum ResponseStatusEnum
{
/// <summary>
/// 程序执行正常,code 200
/// </summary>
Ok = 200,
/// <summary>
/// 参数为null或为空,code 201
/// </summary>
ArgumentsIsNull = 201,
/// <summary>
/// 程序执行发生异常,code 202
/// </summary>
ProgramExecuteOccurException = 202
}
[Description("响应信息")]
public class Response<T>
{
/// <summary>
/// 错误码,默认200
/// 200正常,201参数为空,202程序执行异常
/// </summary>
[Description("错误码")]
public string ErrorCode { get; set; } = ((int)ResponseStatusEnum.Ok).ToString();
[Description("错误信息")]
public string ErrorMsg { get; set; } = "";
[Description("响应结果是否成功")]
public bool IsSuccess
{
get { return string.IsNullOrWhiteSpace(this.ErrorMsg); }
}
[Description("结果集合")]
public T Data { get; set; }
}
public class InspectSwitchViewModel<T>
{
public T Result { get; set; }
}
public partial class Form2 : Form
{
//Api input model
private InspectContentExtractReturnInputModel _inputModel = new InspectContentExtractReturnInputModel();
public Form2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
_inputModel = new InspectContentExtractReturnInputModel()
{
Datas = new List<string>()
{
"params1","params2","params3"
}
};
Task.Run(async () =>
{
var r = await ApiHttpUtils.ExecuteHttpClientAsync(
new Uri(SystemConfig.TestTimeConsumWebApiUrl),//server api url
HttpMethodEnum.Post, //http method type
DataTransferExtensions.SetTransferDataJson(_inputModel), //input model 序列为json
"json",//input params type:json
"json"//return params type:json
);
//以下是与服务端预定好的返回值数据结构,将json反序列化为Model
var response = DataTransferExtensions.GetTransferData<Response<InspectSwitchViewModel<object>>>(r);
if (response.IsSuccess &&
response.ErrorCode == ((int) ResponseStatusEnum.Ok).ToString())//判断服务端响应结果
{
this.Invoke(new Action(() =>
{
this.label1.Text = response.Data.Result.ToString();//更新winform 界面UI提示信息
}));
}
else
{
this.Invoke(new Action(() =>
{
this.label1.Text = response.ErrorMsg;//更新winform 界面UI提示信息
}));
}
});
}
}
4.Server是使用的ASP.NET WebAPI,示例代码如下:
public class InspectSwitchControllerService
{
public async Task<Response<InspectSwitchViewModel<object>>> InspectContentExtractReturn(List<string> datas)
{
Response<InspectSwitchViewModel<object>> r = new Response<InspectSwitchViewModel<object>>();
try
{
//await Task.Delay(1000000);//此处编写业务实现代码
}
catch (Exception ex)
{
r.ErrorCode = ((int) ResponseStatusEnum.ProgramExecuteOccurException).ToString();
r.ErrorMsg = ex.Message;
}
return r;
}
}
public class InspectSwitchController : ApiController
{
private readonly InspectSwitchControllerService _inspectSwitchControllerService = new InspectSwitchControllerService();
[HttpPost]
public IHttpActionResult InspectContentExtractReturn([FromBody] InspectContentExtractReturnInputModel model)
{
var response = new Response<InspectSwitchViewModel<object>>()
{
ErrorCode = ((int)ResponseStatusEnum.Ok).ToString(),
ErrorMsg = string.Empty,
Data = new InspectSwitchViewModel<object>(){Result= "调用成功,直接返回结果,不等待耗时任务完成即返回" }
};
Task.Run(async () =>
{
await _inspectSwitchControllerService.InspectContentExtractReturn(model.Datas);
});
return Ok(response);
}
}