.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">POSTPUTPatch方式时,传值参数</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);
        }
    }

原文地址:https://www.cnblogs.com/jeff151013/p/14704576.html