场景
Winform中怎样使用HttpClient调用http的get和post接口并将接口返回json数据解析为实体类:
Winform中怎样使用HttpClient调用http的get和post接口并将接口返回json数据解析为实体类_winform请求http接口_霸道流氓气质的博客-
参考前面使用HttpClient调用http的get和post接口的小示例,
需要定位调用http的get接口并对接口返回数据进行后续处理。
关于定时器的使用在下面文章中有涉及到
Winform中使用mysqldump实现选择部分表定期备份mysql数据库:
Winform中使用mysqldump实现选择部分表定期备份mysql数据库_mysqldump 部分表_霸道流氓气质的博客-
注:
博客:霸道流氓气质的博客_C#,架构之路,SpringBoot领域博主
实现
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的服务端和客户端之间的通信以及将订阅的消息保存到文件:
Winform中使用MQTTnet实现MQTT的服务端和客户端之间的通信以及将订阅的消息保存到文件_mqtt winform_霸道流氓气质的博客
4、异步
同步和异步主要用于修饰方法。当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,
我们称这个方法是同步方法;当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,
调用者不用等待该方法执行完毕,我们称这个方法为异步方法。
异步的好处在于非阻塞(调用线程不会暂停执行去等待子线程完成),因此我们把一些不需要立即使用结果、
较耗时的任务设为异步执行,可以提高程序的运行效率。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#中全局作用域的常量、字段、属性、方法的定义与使用:
C#中全局作用域的常量、字段、属性、方法的定义与使用_霸道流氓气质的博客-
这里就一个窗体,所以在窗体初始化后设置其响应时长,也可放在全局工具类的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://blog.51cto.com/BADAOLIUMANGQZ/6149293