在工业自动化和物联网项目中,你是否遇到过这样的困扰:需要与各种设备API进行通信,但每次都要重复编写HTTP请求代码?面对复杂的认证机制和数据格式转换,总是要花费大量时间调试?
今天就来分享一个实战级的C# HTTP API客户端封装方案,以Kepware工业通信软件为例,教你构建一个通用、易用、可复用的API客户端框架。这套方案已在多个工业项目中验证,能大幅提升开发效率!
核心思路:将HTTP通信逻辑统一封装,对外提供简洁的调用接口。
C#public class KepwareApiClient
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
public KepwareApiClient(string baseUrl, string username, string password)
{
_baseUrl = baseUrl.TrimEnd('/');
// 配置SSL证书验证(生产环境需谨慎使用)
var handler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};
_httpClient = new HttpClient(handler);
// 设置Basic Auth认证
var authToken = Encoding.ASCII.GetBytes($"{username}:{password}");
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken));
// 设置JSON content type
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
}
应用场景:适用于所有需要HTTP Basic Auth的工业设备API通信
避坑提醒:⚠️ SSL证书验证跳过仅适用于测试环境,生产环境务必进行证书验证
设计亮点:每个HTTP方法都包含完整的日志输出和异常处理
C#// GET 请求封装
public async Task<string> GetAsync(string endpoint)
{
try
{
var response = await _httpClient.GetAsync($"{_baseUrl}{endpoint}");
var content = await response.Content.ReadAsStringAsync();
// 格式化输出,便于调试
Console.WriteLine($"GET {endpoint}");
Console.WriteLine($"Status: {response.StatusCode}");
Console.WriteLine($"Response: {FormatJson(content)}");
Console.WriteLine(new string('-', 80));
return content;
}
catch (Exception ex)
{
Console.WriteLine($"GET {endpoint} - Error: {ex.Message}");
return null;
}
}
// POST 请求封装
public async Task<string> PostAsync(string endpoint, object data = null)
{
try
{
var json = data != null ? JsonConvert.SerializeObject(data, Formatting.Indented) : "";
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content);
var responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"POST {endpoint}");
if (data != null) Console.WriteLine($"Request Body: {json}");
Console.WriteLine($"Status: {response.StatusCode}");
Console.WriteLine($"Response: {FormatJson(responseContent)}");
return responseContent;
}
catch (Exception ex)
{
Console.WriteLine($"POST {endpoint} - Error: {ex.Message}");
return null;
}
}
实用技巧:
JsonConvert.SerializeObject
自动处理对象序列化让调试输出更友好,一眼就能看懂API响应结构:
C#private string FormatJson(string json)
{
if (string.IsNullOrEmpty(json)) return "Empty response";
try
{
var parsedJson = JToken.Parse(json);
return parsedJson.ToString(Formatting.Indented);
}
catch
{
return json; // 如果不是有效JSON,返回原始内容
}
}
优势:自动识别JSON格式并美化输出,非JSON内容也能正常显示
将API测试逻辑按功能模块分组,代码结构更清晰:
C#// 通道操作示例
private static async Task TestChannelApis()
{
Console.WriteLine("=== 通道API测试 ===");
// 创建Modbus通道
var channelConfig = new Dictionary<string, string>
{
["servermain.MULTIPLE_TYPES_DEVICE_DRIVER"] = "Modbus TCP/IP Ethernet",
["common.ALLTYPES_NAME"] = "Modbus_Channel"
};
await _apiClient.PostAsync("/config/v1/project/channels", channelConfig);
// 获取通道列表验证创建结果
await _apiClient.GetAsync("/config/v1/project/channels");
}
// 设备操作示例
private static async Task TestDeviceApis()
{
Console.WriteLine("=== 设备API测试 ===");
var deviceConfig = new Dictionary<string, string>
{
["servermain.MULTIPLE_TYPES_DEVICE_DRIVER"] = "Simulator",
["common.ALLTYPES_NAME"] = "TestDevice"
};
await _apiClient.PostAsync("/config/v1/project/channels/LMES/devices", deviceConfig);
await _apiClient.GetAsync("/config/v1/project/channels/LMES/devices/TestDevice");
}
模块化优势:
确保HTTP连接正确释放,避免资源泄露:
C#public void Dispose()
{
_httpClient?.Dispose();
}
// 在Main方法中正确使用
public static async Task Main(string[] args)
{
_apiClient = new KepwareApiClient(baseUrl, username, password);
try
{
await TestBasicApis();
await TestChannelApis();
// ... 其他测试
}
finally
{
_apiClient.Dispose(); // 确保资源释放
}
}
C#
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace KepwareConfigApiTester
{
public class KepwareApiClient
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
private readonly string _username;
private readonly string _password;
public KepwareApiClient(string baseUrl, string username, string password)
{
_baseUrl = baseUrl.TrimEnd('/');
_username = username;
_password = password;
_httpClient = new HttpClient();
// 设置Basic Auth
var authToken = Encoding.ASCII.GetBytes($"{_username}:{_password}");
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken));
// 设置Content-Type
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// 忽略SSL证书验证(仅用于测试环境)
var handler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};
_httpClient = new HttpClient(handler);
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken));
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
// GET 请求
public async Task<string> GetAsync(string endpoint)
{
try
{
var response = await _httpClient.GetAsync($"{_baseUrl}{endpoint}");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"GET {endpoint}");
Console.WriteLine($"Status: {response.StatusCode}");
Console.WriteLine($"Response: {FormatJson(content)}");
Console.WriteLine(new string('-', 80));
return content;
}
catch (Exception ex)
{
Console.WriteLine($"GET {endpoint} - Error: {ex.Message}");
Console.WriteLine(new string('-', 80));
return null;
}
}
// POST 请求
public async Task<string> PostAsync(string endpoint, object data = null)
{
try
{
var json = data != null ? JsonConvert.SerializeObject(data, Formatting.Indented) : "";
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content);
var responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"POST {endpoint}");
if (data != null) Console.WriteLine($"Request Body: {json}");
Console.WriteLine($"Status: {response.StatusCode}");
Console.WriteLine($"Response: {FormatJson(responseContent)}");
Console.WriteLine(new string('-', 80));
return responseContent;
}
catch (Exception ex)
{
Console.WriteLine($"POST {endpoint} - Error: {ex.Message}");
Console.WriteLine(new string('-', 80));
return null;
}
}
// PUT 请求
public async Task<string> PutAsync(string endpoint, object data)
{
try
{
var json = JsonConvert.SerializeObject(data, Formatting.Indented);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PutAsync($"{_baseUrl}{endpoint}", content);
var responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine($"PUT {endpoint}");
Console.WriteLine($"Request Body: {json}");
Console.WriteLine($"Status: {response.StatusCode}");
Console.WriteLine($"Response: {FormatJson(responseContent)}");
Console.WriteLine(new string('-', 80));
return responseContent;
}
catch (Exception ex)
{
Console.WriteLine($"PUT {endpoint} - Error: {ex.Message}");
Console.WriteLine(new string('-', 80));
return null;
}
}
// DELETE 请求
public async Task<string> DeleteAsync(string endpoint)
{
try
{
var response = await _httpClient.DeleteAsync($"{_baseUrl}{endpoint}");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"DELETE {endpoint}");
Console.WriteLine($"Status: {response.StatusCode}");
Console.WriteLine($"Response: {FormatJson(content)}");
Console.WriteLine(new string('-', 80));
return content;
}
catch (Exception ex)
{
Console.WriteLine($"DELETE {endpoint} - Error: {ex.Message}");
Console.WriteLine(new string('-', 80));
return null;
}
}
private string FormatJson(string json)
{
if (string.IsNullOrEmpty(json)) return "Empty response";
try
{
var parsedJson = JToken.Parse(json);
return parsedJson.ToString(Formatting.Indented);
}
catch
{
return json;
}
}
public void Dispose()
{
_httpClient?.Dispose();
}
}
public class Program
{
private static KepwareApiClient _apiClient;
public static async Task Main(string[] args)
{
Console.WriteLine("Kepware Configuration API 测试程序");
Console.WriteLine("=====================================");
// 配置连接信息
string baseUrl = "http://localhost:57412"; // 根据实际情况修改
string username = "administrator";
string password = "123456";
Console.WriteLine($"连接信息:");
Console.WriteLine($"Base URL: {baseUrl}");
Console.WriteLine($"Username: {username}");
Console.WriteLine($"Password: {password}");
Console.WriteLine();
_apiClient = new KepwareApiClient(baseUrl, username, password);
try
{
// 测试主要的API端点
await TestBasicApis();
await TestChannelApis();
await TestDeviceApis();
await TestTagApis();
await TestTransactionAndEventLogs();
}
catch (Exception ex)
{
Console.WriteLine($"程序执行出错: {ex.Message}");
}
finally
{
_apiClient.Dispose();
}
Console.WriteLine("测试完成,按任意键退出...");
Console.ReadKey();
}
private static async Task TestBasicApis()
{
Console.WriteLine("=== 基础API测试 ===");
// 获取项目信息
await _apiClient.GetAsync("/config/v1/project");
// 获取别名列表
await _apiClient.GetAsync("/config/v1/project/aliases");
// 获取通道列表
await _apiClient.GetAsync("/config/v1/project/channels");
// 获取客户端接口
await _apiClient.GetAsync("/config/v1/project/client_interfaces");
// 获取服务列表
await _apiClient.GetAsync("/config/v1/project/services");
}
private static async Task TestChannelApis()
{
Console.WriteLine("=== 通道API测试 ===");
// 创建测试通道
var anonymousObject = new Dictionary<string, string>
{
["servermain.MULTIPLE_TYPES_DEVICE_DRIVER"] = "Modbus TCP/IP Ethernet",
["common.ALLTYPES_NAME"] = "Modbus"
};
await _apiClient.GetAsync("/config/v1/project/channels");
await _apiClient.PostAsync("/config/v1/project/channels", anonymousObject);
//// 获取特定通道信息
await _apiClient.GetAsync("/config/v1/project/channels/LMES");
//// 获取通道设备列表
await _apiClient.GetAsync("/config/v1/project/channels/LMES/devices");
}
private static async Task TestDeviceApis()
{
Console.WriteLine("=== 设备API测试 ===");
// 创建测试设备,这里对像通过GetAsync方法获取每个设备信息,参考着写这个设备
var anonymousObject = new Dictionary<string, string>
{
["servermain.MULTIPLE_TYPES_DEVICE_DRIVER"] = "Simulator",
["common.ALLTYPES_NAME"] = "device1"
};
await _apiClient.GetAsync("/config/v1/project/channels/LMES/devices");
await _apiClient.PostAsync("/config/v1/project/channels/LMES/devices", anonymousObject);
// 获取特定设备信息
await _apiClient.GetAsync("/config/v1/project/channels/LMES/devices/device1");
// 获取设备标签组
await _apiClient.GetAsync("/config/v1/project/channels/LMES/devices/device1/tag_groups");
// 获取设备标签
await _apiClient.GetAsync("/config/v1/project/channels/LMES/devices/device1/tags");
}
private static async Task TestTagApis()
{
Console.WriteLine("=== 标签API测试 ===");
// 创建标签组
var anonymousObject = new Dictionary<string, object>
{
["servermain.TAG_ADDRESS"] = "RAMP (100, 1.000000, 100.000000, 1.000000)",
["servermain.TAG_DATA_TYPE"] = 8,
["common.ALLTYPES_NAME"] = "Test3"
};
await _apiClient.GetAsync("/config/v1/project/channels/LMES/devices/W1/tags");
await _apiClient.PostAsync("/config/v1/project/channels/LMES/devices/W1/tags", anonymousObject);
// 获取标签信息
await _apiClient.GetAsync("/config/v1/project/channels/LMES/devices/W1/tags/Test3");
}
private static async Task TestTransactionAndEventLogs()
{
Console.WriteLine("=== 日志API测试 ===");
var startTime = DateTime.Now.AddDays(-1).ToString("yyyy-MM-ddTHH:mm:ss.fff");
var endTime = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fff");
await _apiClient.GetAsync($"/config/v1/transaction_log");
// 获取事务日志
await _apiClient.GetAsync($"/config/v1/transaction_log?start={startTime}&end={endTime}&limit=100");
await _apiClient.GetAsync($"/config/v1/event_log");
// 获取事件日志
await _apiClient.GetAsync($"/config/v1/event_log?start={startTime}&end={endTime}&limit=100");
}
// 清理测试数据的方法
private static async Task CleanupTestData()
{
Console.WriteLine("=== 清理测试数据 ===");
// 删除测试标签
await _apiClient.DeleteAsync("/config/v1/project/channels/TestChannel/devices/TestDevice/tag_groups/TestTagGroup/tags/TestTag");
// 删除测试标签组
await _apiClient.DeleteAsync("/config/v1/project/channels/TestChannel/devices/TestDevice/tag_groups/TestTagGroup");
// 删除测试设备
await _apiClient.DeleteAsync("/config/v1/project/channels/TestChannel/devices/TestDevice");
// 删除测试通道
await _apiClient.DeleteAsync("/config/v1/project/channels/TestChannel");
}
}
}
Q: SSL证书验证失败怎么办?
A: 测试环境可以跳过验证,生产环境建议安装正确的证书或使用证书回调验证
Q: 认证失败如何排查?
A: 检查用户名密码是否正确,确认API服务是否启用Basic Auth
Q: JSON序列化出错如何处理?
A: 使用try-catch包装序列化操作,提供降级处理方案
通过这套通用HTTP API客户端框架,我们实现了:
这不仅仅是一个Kepware API的客户端,更是一个可扩展的工业通信框架模板。你可以基于这个架构,快速适配其他设备的API通信需求。
你在工业项目中还遇到过哪些API通信难题?欢迎在评论区分享你的经验,或者说说你希望看到哪些技术方案的深入解析!
觉得这个方案对你的项目有帮助?请点赞并转发给更多需要的同行! 让我们一起用技术驱动工业智能化进程!
相关信息
通过网盘分享的文件:AppKepTool.zip 链接: https://pan.baidu.com/s/1OgKCQikIhDKBudMMcENJTA?pwd=xvwe 提取码: xvwe --来自百度网盘超级会员v9的分享:::
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!