场景
Winform中怎样使用HttpClient调用http的get和post接口并将接口返回json数据解析为实体类:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/124157296
参考前面使用HttpClient调用http的get和post接口的小示例,
需要定位调用http的get接口并对接口返回数据进行后续处理。
关于定时器的使用在下面文章中有涉及到
Winform中使用mysqldump实现选择部分表定期备份mysql数据库:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/120650090
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
实现
1、初次业务实现逻辑比较简单,未考虑任务同步执行堵塞UI线程的情况。
在测试时发现,当http接口不通时,而又未设置httpClient的最大超时响应时间,
会导致页面卡死无响应。
优化前的代码:
private void convertPositionControl() { try { //获取接口数据 var positionData = requestGetHttpData(positionCalculateUrl); //http请求不到数据,啥也不干 if (null == positionData) { return; }//请求到数据,则进行数据处理 else { } } catch (Exception exception) { textBox_log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ":获取接口出错:"); textBox_log.AppendText("\r\n"); textBox_log.AppendText(exception.Message); } }
上面是在定时器中具体执行的代码,省略部分逻辑,其中请求http接口的方法是
requestGetHttpData,参数getAllBaseStationInfoUrl是接口url。
然后请求接口的方法具体实现是
private string requestGetHttpData(string apiUrl) { try { //调用接口请求数据 var originAddressUrl = apiUrl; //请求接口数据 if (null == httpClient) { httpClient = new HttpClient(); } var url = new Uri(originAddressUrl); var response = httpClient.GetAsync(url).Result; var data = response.Content.ReadAsStringAsync().Result; return data; } catch (Exception exception) { textBox_log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ":调用接口" + apiUrl + "异常:" + exception.Message); textBox_log.AppendText("\r\n"); return null; } }
这里直接未作任何考虑,只是考虑接口正常的情况。
但是当接口不存在或者报错时就会导致页面卡死。
注意:
HttpClient的预热机制,不要在每次请求接口时都要初始化
httpClient = new HttpClient();
这里放在页面加载完成之后进行初始化一次
private void Form1_Load(object sender, EventArgs e) { httpClient = new HttpClient(); httpClient.Timeout = TimeSpan.FromSeconds(httpClientMaxResponseSeconds); }
另外需要给httpClient设置最大响应超时时长
2、C# 中 HttpClient设置最大响应超时时长
httpClient.Timeout = TimeSpan.FromSeconds(httpClientMaxResponseSeconds);
这里httpClientMaxResponseSeconds是
private double httpClientMaxResponseSeconds = 2;
这里设置为2秒。
后续建议将其优化为单例模式或其他更好的模式。
3、上面卡住问题是因为在同步执行的方法中,请求接口的方法会堵塞UI线程/主线程。
需要将上面请求接口的方法修改成异步任务执行的机制,避免影响UI线程。
这块在之前写mqtt连接时用到到,但是当时不知其所以然。
Winform中使用MQTTnet实现MQTT的服务端和客户端之间的通信以及将订阅的消息保存到文件:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1177398594、异步
同步和异步主要用于修饰方法。当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,
我们称这个方法是同步方法;当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,
调用者不用等待该方法执行完毕,我们称这个方法为异步方法。
异步的好处在于非阻塞(调用线程不会暂停执行去等待子线程完成),因此我们把一些不需要立即使用结果、
较耗时的任务设为异步执行,可以提高程序的运行效率。net4.0在ThreadPool的基础上推出了Task类,
微软极力推荐使用Task来执行异步任务,现在C#类库中的异步方法基本都用到了Task;net5.0推出了async/await,
让异步编程更为方便。
在C#5.0中出现的 async和await ,让异步编程变得更简单。
关于async Task 和await的使用不再详述,具体可自行学习。
下面参考一个网络上的示例
class Program { static void Main(string[] args) { string content = GetContentAsync(Environment.CurrentDirectory + @"/test.txt").Result; //调用同步方法 //string content = GetContent(Environment.CurrentDirectory + @"/test.txt"); Console.WriteLine(content); Console.ReadKey(); } //异步读取文件内容 async static Task<string> GetContentAsync(string filename) { FileStream fs = new FileStream(filename, FileMode.Open); var bytes = new byte[fs.Length]; //ReadAync方法异步读取内容,不阻塞线程 Console.WriteLine("开始读取文件"); int len = await fs.ReadAsync(bytes, 0, bytes.Length); string result = Encoding.UTF8.GetString(bytes); return result; } //同步读取文件内容 static string GetContent(string filename) { FileStream fs = new FileStream(filename, FileMode.Open); var bytes = new byte[fs.Length]; //Read方法同步读取内容,阻塞线程 int len = fs.Read(bytes, 0, bytes.Length); string result = Encoding.UTF8.GetString(bytes); return result; } }
优化代码
首先将HttpClient修改为单例模式,避免每次请求接口都去new
新建类
{ class Global { private static string _lockFlag = "GlobalLock"; private static Global _instance; //http请求客户端 public HttpClient httpClient = new HttpClient(); private Global() { } public static Global Instance { get { lock (_lockFlag) { if (_instance == null) { _instance = new Global(); } return _instance; } } } } }
关于全局/单例的实现可以参考如下:
C#中全局作用域的常量、字段、属性、方法的定义与使用:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/102550025
这里就一个窗体,所以在窗体初始化后设置其响应时长,也可放在全局工具类的get方法中
private void Form1_Load(object sender, EventArgs e) { //设置http连接超时时间 Global.Instance.httpClient.Timeout = TimeSpan.FromSeconds(httpClientMaxResponseSeconds); }
然后改造请求Http接口的方法
private async Task<string> requestGetHttpData(string apiUrl) { try { var originAddressUrl = apiUrl; //请求接口数据 var url = new Uri(originAddressUrl); string jsonResponse = await Global.Instance.httpClient.GetStringAsync(url); return jsonResponse; } catch (Exception exception) { textBox_log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ":调用接口" + apiUrl + "异常:" + exception.Message); textBox_log.AppendText("\r\n"); return null; } }
将方法添加async Task<string>,注意这里httpClient调用GetStringAsync方法前面加了await
然后定时器执行中的方法调用修改为
private async Task convertPositionControl() { try { //获取接口数据 var positionData = await requestGetHttpData(positionCalculateUrl); //http请求不到数据,啥也不干 if (null == positionData) { return; }//请求到数据,则进行数据处理 else { } } catch (Exception exception) { textBox_log.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ":获取接口出错:"); textBox_log.AppendText("\r\n"); textBox_log.AppendText(exception.Message); } }
至此,不会导致界面卡住的现象。
标签:异步,Task,string,await,接口,new,async,HttpClient,httpClient From: https://www.cnblogs.com/badaoliumangqizhi/p/17252947.html