本文档基于自写的 WebApi 项目编写,讲解 WebApi 通讯的原理、及每个方法的实现细节。
本文只用于对该项目的技术分析。项目为自用项目,源码不对外开源。
目录
1. 项目概述
1.1 项目架构
┌─────────────────────────────────────────────────────────────────┐
│ WinForms 上位机应用程序 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ FrmMain (主窗体) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │ │
│ │ │ 登录按钮 │ │ 项目选择 │ │ 变量选择 │ │读写按钮│ │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └───┬────┘ │ │
│ └───────┼─────────────┼─────────────┼────────────┼────────┘ │
│ │ │ │ │ │
└──────────┼─────────────┼─────────────┼────────────┼────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ DeviceApi.cs (设备API类) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │ Login │ │ GetProject│ │ GetVariable│ │ReadWrite│ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └───┬────┘ │
└───────┼─────────────┼─────────────┼────────────┼────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ WebApiHelper.cs (HTTP封装) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ RestSharp (第三方HTTP库) │ │
│ │ • HttpPost • HttpPostBearerToken │ │
│ │ • HttpGet • 表单提交 │ │
│ └────────────────────┬───────────────────────────────────┘ │
└───────────────────────┼──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 服务器 (API端点) │
│ • 登录认证: /api-uaa/oauth/token │
│ • 获取项目: /api-organization/project/getAuthProjectList │
│ • 变量管理: /api-business/variant/... │
└─────────────────────────────────────────────────────────────────┘
1.2 技术栈
| 技术 | 版本 | 用途 |
|---|---|---|
| .NET Framework | 4.6 | 运行时环境 |
| C# | - | 开发语言 |
| WinForms | - | UI框架 |
| RestSharp | 106.15.0 | HTTP客户端库 |
| Newtonsoft.Json | 13.0.1 | JSON序列化 |
1.3 项目文件结构
WebApi Demo/
├── DeviceApi.cs # 核心业务API类(设备通信)
├── WebApiHelper.cs # HTTP请求封装工具类
├── FrmMain.cs # 主窗体逻辑代码
├── FrmMain.Designer.cs # 主窗体设计器代码
├── Program.cs # 程序入口
├── App.config # 应用配置
└── WebApi Demo.csproj # 项目文件
2. WebApi 通信原理
2.1 HTTP 完整通信流程
┌─────────┐ ┌─────────┐
│ 客户端 │ │ 服务器 │
│Client │ │Server │
└────┬────┘ └────┬────┘
│ │
│ ① 构建HTTP请求 │
│ - 请求行 (Method + URL + Version) │
│ - 请求头 (Headers: Authorization等) │
│ - 请求体 (Body: JSON数据) │
│ │
├────────────────────── ② 发送请求 ──────────────▶│
│ │
│ ③ 处理请求 │
│ - 验证Token │
│ - 业务逻辑 │
│ - 查询数据库 │
│ │
│◀──────────────────── ④ 返回响应 ───────────────┤
│ │
│ ⑤ 解析响应 │
│ - 状态码检查 (200/401/500等) │
│ - JSON反序列化 │
│ - 数据绑定 │
│ │
▼ ▼
2.2 HTTP 请求报文结构
POST /api-uaa/oauth/token HTTP/1.1 ← 请求行
Host: www.sukon-cloud.com ← 请求头
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Length: 125
{"keyId":"xxx","keySecret":"yyy"} ← 请求体
2.3 HTTP 响应报文结构
HTTP/1.1 200 OK ← 状态行
Content-Type: application/json ← 响应头
Content-Length: 356
{ ← 响应体
"code": "200",
"msg": "success",
"data": {...},
"time": "2026-04-06T10:30:00"
}
2.4 RESTful API 设计规范
资源路径设计规范:
┌─────────────────────────────────────────────────────────────┐
│ ✓ 正确示例: │
│ POST /api-uaa/oauth/token # 登录获取Token │
│ POST /api-organization/project/... # 获取项目列表 │
│ POST /api-business/variant/... # 变量读写操作 │
│ │
│ ✓ URL层次清晰: │
│ /{模块}/{资源}/{操作} │
│ /api-uaa/oauth/token │
│ │ │ │ │
│ │ │ └── 操作: token │
│ │ └──────── 认证模块 │
│ └──────────────── API标识 │
└─────────────────────────────────────────────────────────────┘
3. 核心技术详解
3.1 RestSharp 库原理
RestSharp 是一个简化 HTTP 请求的第三方库,其工作原理:
┌─────────────────────────────────────────────────────────────┐
│ RestSharp 工作流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ① 创建 RestClient │
│ ↓ │
│ RestClient client = new RestClient(url); │
│ └── 设置基础URL、超时时间等 │
│ │
│ ② 创建 RestRequest │
│ ↓ │
│ RestRequest request = new RestRequest(Method.POST); │
│ └── 设置HTTP方法、添加请求头、添加参数 │
│ │
│ ③ 配置请求 │
│ ↓ │
│ request.AddHeader("Content-Type", "application/json"); │
│ request.AddJsonBody(data); │
│ │
│ ④ 执行请求 │
│ ↓ │
│ IRestResponse response = client.Execute(request); │
│ └── 发送请求并接收响应 │
│ │
│ ⑤ 处理响应 │
│ ↓ │
│ string content = response.Content; │
│ int statusCode = (int)response.StatusCode; │
│ │
└─────────────────────────────────────────────────────────────┘
3.2 JSON 序列化原理
┌─────────────────────────────────────────────────────────────┐
│ JSON 序列化/反序列化 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 序列化 (对象 → JSON字符串): │
│ ┌─────────────────┐ ┌─────────────────────────────┐ │
│ │ C# 对象 │ ───▶ │ {"name":"value","age":25} │ │
│ └─────────────────┘ └─────────────────────────────┘ │
│ │ │
│ ▼ │
│ JsonConvert.SerializeObject(obj) │
│ │
│ 反序列化 (JSON字符串 → 对象): │
│ ┌─────────────────────────────┐ ┌─────────────────┐ │
│ │ {"name":"value","age":25} │ ───▶ │ C# 对象 │ │
│ └─────────────────────────────┘ └─────────────────┘ │
│ │ │
│ ▼ │
│ JsonConvert.DeserializeObject<T>(json) │
│ │
└─────────────────────────────────────────────────────────────┘
3.3 Bearer Token 认证原理
┌─────────────────────────────────────────────────────────────┐
│ Bearer Token 认证流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 第1步: 客户端登录请求 │
│ ┌────────────────────────────────────────────────────┐ │
│ │ POST /api-uaa/oauth/token │ │
│ │ Body: { │ │
│ │ "keyId": "用户ID", │ │
│ │ "keySecret": "密钥", │ │
│ │ "grant_type": "key_secret" │ │
│ │ } │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 第2步: 服务器验证并返回Token │
│ ┌────────────────────────────────────────────────────┐ │
│ │ { │ │
│ │ "code": "200", │ │
│ │ "data": { │ │
│ │ "access_token": "eyJhbGc...", // 访问令牌 │ │
│ │ "token_type": "Bearer", │ │
│ │ "expires_in": 7200 │ │
│ │ } │ │
│ │ } │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 第3步: 后续请求携带Token │
│ ┌────────────────────────────────────────────────────┐ │
│ │ POST /api-business/variant/... │ │
│ │ Headers: │ │
│ │ Authorization: Bearer eyJhbGc... │ │
│ └────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
4. 代码逐行解析
4.1 WebApiHelper.cs 完整解析
这是项目的 HTTP 通信核心工具类。
方法一:HttpPost - 发送JSON数据
csharp
/// <summary>
/// 发送JSON格式的POST请求(泛型方法)
/// </summary>
/// <typeparam name="T">请求体类型</typeparam>
/// <param name="url">API地址</param>
/// <param name="body">请求体对象</param>
/// <returns>响应对象</returns>
public static IRestResponse HttpPost<T>(string url, T body)
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第1步: 创建 RestClient (HTTP客户端)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
RestClient client = new RestClient(url);
// │
// └── RestSharp的核心类,负责管理HTTP连接
//
// new RestClient(url) 做了什么:
// • 解析URL,提取协议(http/https)、主机名、端口
// • 初始化内部的HttpClient
// • 设置默认的超时时间
client.Timeout = 5000;
// │
// └── 设置请求超时时间为5000毫秒(5秒)
// 超过5秒未收到响应将抛出异常
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第2步: 创建 RestRequest (HTTP请求)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
RestRequest request = new RestRequest(Method.POST);
// │
// └── 创建一个POST类型的请求对象
//
// Method.POST 枚举值包括:
// • GET - 获取数据
// • POST - 创建数据
// • PUT - 更新数据
// • DELETE - 删除数据
// • PATCH - 部分更新
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第3步: 设置请求头
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
request.AddHeader("Content-Type", "application/json");
// │
// └── 告诉服务器: 我发送的是JSON格式数据
//
// 常见Content-Type:
// • application/json - JSON数据
// • application/xml - XML数据
// • application/x-www-form-urlencoded - 表单数据
// • multipart/form-data - 文件上传
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第4步: 添加请求体
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
request.AddJsonBody(body, "application/json");
// │
// └── 将对象序列化为JSON并添加到请求体
//
// AddJsonBody做了什么:
// 1. 使用Json.NET将对象序列化为JSON字符串
// 2. 设置Content-Type为application/json
// 3. 将JSON字符串写入请求体
//
// 示例: 如果body是一个User对象
// 序列化后: {"name":"张三","age":25}
// 最终HTTP请求体: {"name":"张三","age":25}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第5步: 执行请求
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
return client.Execute(request);
// │
// └── 同步执行HTTP请求,返回响应
//
// 返回的IRestResponse包含:
// • StatusCode - HTTP状态码(200/401/500等)
// • Content - 响应体内容(字符串)
// • Headers - 响应头集合
// • ErrorMessage - 错误信息(如果失败)
}
实际发送的HTTP请求:
http
POST /api-xxx HTTP/1.1
Host: www.xxx.com
Content-Type: application/json
Content-Length: 45
{"keyId":"xxx","keySecret":"yyy"}
方法二:HttpPostBearerToken - 带Token的POST请求
csharp
/// <summary>
/// 发送携带Bearer Token的POST请求
/// </summary>
public static IRestResponse HttpPostBearerToken<T>(string url, string token, T body)
{
// 创建客户端和请求对象
RestClient client = new RestClient(url);
client.Timeout = 5000;
RestRequest request = new RestRequest(Method.POST);
// 设置Content-Type
request.AddHeader("Content-Type", "application/json");
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 关键: 添加认证头
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
request.AddHeader("Authorization", $"Bearer {token}");
// │
// └── 添加OAuth 2.0标准的认证头
//
// 格式: Authorization: Bearer <token>
//
// Bearer Token认证原理:
// 1. 用户先登录获取access_token
// 2. 后续请求在Header中携带token
// 3. 服务器验证token有效性
// 4. 验证通过后处理业务逻辑
// 添加JSON请求体
request.AddJsonBody(body, "application/json");
// 执行请求
return client.Execute(request);
}
实际发送的HTTP请求:
http
POST /api-xxx/xxx/xxx HTTP/1.1
Host: www.xxx.com
Content-Type: application/json
Authorization: Bearer xxx...
{"variantId":"123","value":"45.6"}
方法三:HttpPost - 表单数据提交
csharp
/// <summary>
/// 发送表单格式的POST请求
/// </summary>
public static IRestResponse HttpPost(string url, Dictionary<string, string> para)
{
RestClient client = new RestClient(url);
client.Timeout = 5000;
RestRequest request = new RestRequest(Method.POST);
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 关键: 设置为表单数据模式
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
request.AddHeader("Content-Type", "multipart/form-data");
request.AlwaysMultipartFormData = true;
// │
// └── 启用多部分表单数据模式
//
// multipart/form-data 格式:
// • 用于文件上传
// • 也可用于提交表单字段
// • 数据以边界分隔符分隔
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 添加表单参数
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
foreach (string key in para.Keys)
{
request.AddParameter(key, para[key]);
// │
// └── 添加表单字段
//
// AddParameter做了什么:
// • 将键值对添加到请求体
// • 自动处理编码
}
return client.Execute(request);
}
实际发送的HTTP请求:
http
POST /xxx/oauth/token HTTP/1.1
Host: www.xxx.com
Content-Type: multipart/form-data; boundary=----xxx
Content-Length: 250
------xxx
Content-Disposition: form-data; name="keyId"
xxx
------xxx
Content-Disposition: form-data; name="keySecret"
xxx
------xxx--
方法四:HttpGet - GET请求
csharp
/// <summary>
/// 发送GET请求
/// </summary>
public static IRestResponse HttpGet<T>(string url)
{
RestClient client = new RestClient(url);
client.Timeout = 5000;
RestRequest request = new RestRequest(Method.GET);
// │
// └── GET请求通常不需要请求体
// 数据通过URL参数传递
return client.Execute(request);
}
实际发送的HTTP请求:
http
GET /xxx/xxx/xxx?id=123 HTTP/1.1
Host: www.xxx.com
4.2 DeviceApi.cs 完整解析
这是业务API封装类,包含所有业务接口调用。
构造函数 - API地址初始化
csharp
public class DeviceApi
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// API端点定义
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
private string LoginUrl = string.Empty; // 登录API地址
private string ProjectUrl = string.Empty; // 项目API地址
private string VariableUrl = string.Empty; // 变量API地址
private string ReadUrl = string.Empty; // 读取API地址
private string WriteUrl = string.Empty; // 写入API地址
/// <summary>
/// 构造函数 - 初始化所有API端点地址
/// </summary>
/// <param name="url">服务器基础地址,如: https://www.xxx.com</param>
public DeviceApi(string url)
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 字符串插值构建完整URL
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
this.LoginUrl = $"{url}/api-uaa/oauth/token";
// │ └── 登录认证模块
// └── 基础URL
//
// 结果: https://www.xxx.com/api-uaa/oauth/token
this.ProjectUrl = $"{url}/api-organization/project/getAuthProjectList";
// 结果: https://www.xxx.com/api-organization/project/getAuthProjectList
this.VariableUrl = $"{url}/api-business/variant/getVariantListByProjectIds";
this.ReadUrl = $"{url}/api-business/variant/getVariantRealtimeValueList";
this.WriteUrl = $"{url}/api-business/variant/writeVariantValue";
}
}
方法一:Login - 登录获取Token
csharp
/// <summary>
/// 登录方法 - 获取访问令牌
/// </summary>
/// <param name="uid">用户ID (keyId)</param>
/// <param name="sid">密钥 (keySecret)</param>
/// <returns>操作结果,包含Token信息</returns>
public OperateResult<ResultTokenInfo> Login(string uid, string sid)
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第1步: 构建登录参数字典
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("keyId", uid);
// │ └── 传入的用户ID
// └── 参数名: 服务端定义的字段名
dic.Add("keySecret", sid);
dic.Add("grant_type", "key_secret");
// │
// └── OAuth 2.0 授权类型
// • password - 密码模式
// • client_credentials - 客户端凭证模式
// • key_secret - 自定义模式(速控云专用)
dic.Add("client_id", "webApp");
dic.Add("client_secret", "webApp");
try
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第2步: 调用HTTP请求
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var result = WebApiHelper.HttpPost(this.LoginUrl, dic);
// │
// └── 调用表单POST方法
//
// 发送的HTTP请求:
// POST https://www.xxx.com/api-uaa/oauth/token
// Content-Type: multipart/form-data
//
// keyId=xxx...&xxx=xxx...&xxx=xxx...
try
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第3步: JSON反序列化
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
return OperateResult.CreateSuccessResult(
JsonConvert.DeserializeObject<ResultTokenInfo>(result.Content)
);
// │
// └── 反序列化过程:
// 1. result.Content 是JSON字符串
// 2. JsonConvert.DeserializeObject 将其转换为ResultTokenInfo对象
// 3. OperateResult.CreateSuccessResult 包装成成功结果
}
catch (Exception ex)
{
return OperateResult.CreateFailResult<ResultTokenInfo>("解析错误:" + ex.Message);
}
}
catch (Exception ex)
{
return OperateResult.CreateFailResult<ResultTokenInfo>("Http响应错误:" + ex.Message);
}
}
响应数据结构:
json
{
"code": "200",
"msg": "success",
"data": {
"access_token": "xxx...",
"token_type": "Bearer",
"refresh_token": "...",
"expires_in": 7200,
"scope": "all"
},
"time": "2026-04-06T10:30:00"
}
方法二:GetAuthProjectList - 获取项目列表
csharp
/// <summary>
/// 获取所有项目列表
/// </summary>
/// <param name="token">访问令牌</param>
/// <returns>操作结果,包含项目列表</returns>
public OperateResult<ResultListProjectVo> GetAuthProjectList(string token)
{
try
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 调用带Token的POST请求
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var result = WebApiHelper.HttpPostBearerToken(
this.ProjectUrl,
token,
new QueryProjectListAllParam()
{
pageSize = 10
// └── 分页参数: 每页10条记录
}
);
// 发送的HTTP请求:
// POST https://www.xxx.com/api-organization/project/getAuthProjectList
// Authorization: Bearer xxx...
// Content-Type: application/json
//
// {"pageSize":10}
try
{
return OperateResult.CreateSuccessResult(
JsonConvert.DeserializeObject<ResultListProjectVo>(result.Content)
);
}
catch (Exception ex)
{
return OperateResult.CreateFailResult<ResultListProjectVo>("解析错误:" + ex.Message);
}
}
catch (Exception ex)
{
return OperateResult.CreateFailResult<ResultListProjectVo>("Http响应错误:" + ex.Message);
}
}
方法三:GetVariantRealtimeValueList - 读取变量值
csharp
/// <summary>
/// 读取变量的实时值
/// </summary>
/// <param name="token">访问令牌</param>
/// <param name="variantIds">变量ID列表</param>
/// <returns>操作结果,包含变量值列表</returns>
public OperateResult<ResultListVariantRealtimeValue> GetVariantRealtimeValueList(
string token,
List<string> variantIds)
{
try
{
var result = WebApiHelper.HttpPostBearerToken(
this.ReadUrl,
token,
new VariableIds()
{
variantIds = variantIds
// └── 例如: ["device1:100", "device1:101"]
}
);
try
{
return OperateResult.CreateSuccessResult(
JsonConvert.DeserializeObject<ResultListVariantRealtimeValue>(result.Content)
);
}
catch (Exception ex)
{
return OperateResult.CreateFailResult<ResultListVariantRealtimeValue>("解析错误:" + ex.Message);
}
}
catch (Exception ex)
{
return OperateResult.CreateFailResult<ResultListVariantRealtimeValue>("Http响应错误:" + ex.Message);
}
}
方法四:WriteVariantValues - 写入变量值
csharp
/// <summary>
/// 写入变量值
/// </summary>
/// <param name="token">访问令牌</param>
/// <param name="deviceId">设备ID</param>
/// <param name="variantId">变量ID</param>
/// <param name="value">要写入的值</param>
/// <returns>操作结果</returns>
public OperateResult<ResultObject> WriteVariantValues(
string token,
string deviceId,
int variantId,
string value)
{
try
{
var result = WebApiHelper.HttpPostBearerToken(
this.WriteUrl,
token,
new WriteVariantValue()
{
deviceId = deviceId,
variantId = variantId,
value = value
}
);
try
{
return OperateResult.CreateSuccessResult(
JsonConvert.DeserializeObject<ResultObject>(result.Content)
);
}
catch (Exception ex)
{
return OperateResult.CreateFailResult<ResultObject>("解析错误:" + ex.Message);
}
}
catch (Exception ex)
{
return OperateResult.CreateFailResult<ResultObject>("Http响应错误:" + ex.Message);
}
}
4.3 FrmMain.cs 完整解析
主窗体业务逻辑代码。
登录按钮事件
csharp
/// <summary>
/// 登录按钮点击事件 - 获取Token
/// </summary>
private void btn_Login_Click(object sender, EventArgs e)
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第1步: 调用登录API
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var result = this.deviceApi.Login(uid, sid);
// │
// └── uid和sid在构造函数中已初始化
// uid = "a380648f17c54207ad7c98b71a2f9001"
// sid = "e052a15f87c74a3883a20c9cb864b3bc"
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第2步: 检查是否成功
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
if (result.IsSuccess)
// │
// └── OperateResult类的属性
// IsSuccess = true 表示API调用成功
{
if (result.Content.data != null)
// │ │
// │ └── ResultTokenInfo.data 属性
// └── ResultTokenInfo类型的数据
{
token = result.Content.data.access_token;
// │ │ │ └── TokenInfo.access_token
// │ │ └── ResultTokenInfo.data
// │ └── ResultTokenInfo
// └── 保存Token供后续使用
this.lbl_Token.Text = result.Content.data.access_token;
// │
// └── 在界面显示Token
}
else
{
MessageBox.Show("登录失败:Data为空!", "登陆失败");
}
}
else
{
MessageBox.Show("登录失败:" + result.Message, "登陆失败");
}
}
读取变量按钮事件
csharp
/// <summary>
/// 读取变量按钮点击事件
/// </summary>
private void btn_Read_Click(object sender, EventArgs e)
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第1步: 获取下拉框绑定的变量列表
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
List<VariantByIdVo> varlist = (List<VariantByIdVo>)this.cmb_Variable.DataSource;
// │
// └── 拆箱: 将object类型转换为List<VariantByIdVo>
// DataSource是object类型,需要强制转换
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第2步: 构建变量ID列表
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
List<string> variantIds = new List<string>();
foreach (var item in varlist)
{
variantIds.Add(item.deviceId + ":" + item.variantId);
// │
// └── 变量ID格式: "设备ID:变量ID"
// 例如: "PLC001:100"
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第3步: 调用读取API
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var result = this.deviceApi.GetVariantRealtimeValueList(this.token, variantIds);
if (result.IsSuccess)
{
if (result.Content.data != null && result.Content.data.Count > 0)
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第4步: 将读取的值填充到原列表中
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
foreach (var variable in result.Content.data)
{
foreach (var item in varlist)
{
if (variable.variantId == item.deviceId + ":" + item.variantId)
{
item.value = variable.value;
// │
// └── 将API返回的实时值填充到变量对象中
}
}
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第5步: 绑定到DataGridView显示
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
this.dgv_Data.DataSource = null;
this.dgv_Data.DataSource = varlist;
// │
// └── DataGridView会自动显示属性
// DataPropertyName绑定了variantId, variantName, value
}
else
{
MessageBox.Show("读取变量值失败:Data为空!", "读取变量值失败");
}
}
else
{
MessageBox.Show("读取变量值失败:" + result.Message, "读取变量值失败");
}
}
5. 五种调用方法详解
方法对比表
| 方法名 | HTTP方法 | Content-Type | 认证方式 | 用途 |
|---|---|---|---|---|
HttpPost<T> |
POST | application/json | 无 | 发送JSON数据 |
HttpPostBearerToken<T> |
POST | application/json | Bearer Token | 需认证的JSON请求 |
HttpPost(Dictionary) |
POST | multipart/form-data | 无 | 表单提交 |
HttpGet<T> |
GET | - | 无/Token | 获取数据 |
方法1: HttpPost - JSON提交
csharp
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 定义请求数据模型
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
public class UserData
{
public string Name { get; set; }
public int Age { get; set; }
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 调用示例
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var userData = new UserData { Name = "张三", Age = 25 };
var response = WebApiHelper.HttpPost("https://api.example.com/user", userData);
// 实际发送的HTTP请求:
// POST /user HTTP/1.1
// Host: api.example.com
// Content-Type: application/json
//
// {"Name":"张三","Age":25}
方法2: HttpPostBearerToken - 带Token的JSON提交
csharp
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 调用示例
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
string token = "xxx...";
var requestData = new { variantId = "123", value = "45.6" };
var response = WebApiHelper.HttpPostBearerToken(
"https://api.example.com/write",
token,
requestData
);
// 实际发送的HTTP请求:
// POST /write HTTP/1.1
// Host: api.example.com
// Content-Type: application/json
// Authorization: Bearer xxx...
//
// {"variantId":"123","value":"45.6"}
方法3: HttpPost - 表单提交
csharp
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 调用示例
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var formData = new Dictionary<string, string>
{
{ "username", "admin" },
{ "password", "123456" },
{ "grant_type", "password" }
};
var response = WebApiHelper.HttpPost("https://api.example.com/login", formData);
// 实际发送的HTTP请求:
// POST /login HTTP/1.1
// Host: api.example.com
// Content-Type: multipart/form-data; boundary=----Boundary
//
// ------Boundary
// Content-Disposition: form-data; name="username"
//
// admin
// ------Boundary
// Content-Disposition: form-data; name="password"
//
// 123456
// ------Boundary--
方法4: HttpGet - GET请求
csharp
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 调用示例
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var response = WebApiHelper.HttpGet<string>("https://api.example.com/user/123");
// 实际发送的HTTP请求:
// GET /user/123 HTTP/1.1
// Host: api.example.com
6. 实战代码模板
6.1 完整的API调用流程模板
csharp
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections.Generic;
namespace MyProject.WebApi
{
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第1步: 定义数据模型
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/// <summary>
/// 统一响应格式
/// </summary>
public class ApiResponse<T>
{
public string code { get; set; }
public string msg { get; set; }
public T data { get; set; }
public DateTime time { get; set; }
}
/// <summary>
/// 用户数据模型
/// </summary>
public class UserInfo
{
public string userId { get; set; }
public string userName { get; set; }
public string email { get; set; }
}
/// <summary>
/// Token信息
/// </summary>
public class TokenInfo
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第2步: 封装HTTP工具类
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
public class HttpClientHelper
{
/// <summary>
/// POST请求 - 发送JSON
/// </summary>
public static IRestResponse PostJson<T>(string url, T data)
{
var client = new RestClient(url);
client.Timeout = 10000; // 10秒超时
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddJsonBody(data);
return client.Execute(request);
}
/// <summary>
/// POST请求 - 带Token
/// </summary>
public static IRestResponse PostJsonWithToken<T>(string url, string token, T data)
{
var client = new RestClient(url);
client.Timeout = 10000;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", $"Bearer {token}");
request.AddJsonBody(data);
return client.Execute(request);
}
/// <summary>
/// GET请求
/// </summary>
public static IRestResponse Get(string url)
{
var client = new RestClient(url);
client.Timeout = 10000;
var request = new RestRequest(Method.GET);
return client.Execute(request);
}
/// <summary>
/// GET请求 - 带Token
/// </summary>
public static IRestResponse GetWithToken(string url, string token)
{
var client = new RestClient(url);
client.Timeout = 10000;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", $"Bearer {token}");
return client.Execute(request);
}
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第3步: 封装业务API类
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
public class MyApiClient
{
private string baseUrl;
private string token;
public MyApiClient(string baseUrl)
{
this.baseUrl = baseUrl;
}
/// <summary>
/// 登录
/// </summary>
public ApiResponse<TokenInfo> Login(string username, string password)
{
try
{
var loginData = new
{
username = username,
password = password,
grant_type = "password"
};
var response = HttpClientHelper.PostJson(
$"{baseUrl}/api/auth/login",
loginData
);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var result = JsonConvert.DeserializeObject<ApiResponse<TokenInfo>>(response.Content);
if (result?.data != null)
{
token = result.data.access_token;
}
return result;
}
else
{
return new ApiResponse<TokenInfo>
{
code = ((int)response.StatusCode).ToString(),
msg = $"HTTP错误: {response.StatusCode}"
};
}
}
catch (Exception ex)
{
return new ApiResponse<TokenInfo>
{
code = "500",
msg = $"异常: {ex.Message}"
};
}
}
/// <summary>
/// 获取用户列表
/// </summary>
public ApiResponse<List<UserInfo>> GetUserList()
{
try
{
if (string.IsNullOrEmpty(token))
{
return new ApiResponse<List<UserInfo>>
{
code = "401",
msg = "未登录,请先调用Login方法"
};
}
var response = HttpClientHelper.GetWithToken(
$"{baseUrl}/api/user/list",
token
);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
return JsonConvert.DeserializeObject<ApiResponse<List<UserInfo>>>(response.Content);
}
else
{
return new ApiResponse<List<UserInfo>>
{
code = ((int)response.StatusCode).ToString(),
msg = $"HTTP错误: {response.StatusCode}"
};
}
}
catch (Exception ex)
{
return new ApiResponse<List<UserInfo>>
{
code = "500",
msg = $"异常: {ex.Message}"
};
}
}
/// <summary>
/// 创建用户
/// </summary>
public ApiResponse<object> CreateUser(UserInfo user)
{
try
{
if (string.IsNullOrEmpty(token))
{
return new ApiResponse<object>
{
code = "401",
msg = "未登录"
};
}
var response = HttpClientHelper.PostJsonWithToken(
$"{baseUrl}/api/user/create",
token,
user
);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
return JsonConvert.DeserializeObject<ApiResponse<object>>(response.Content);
}
else
{
return new ApiResponse<object>
{
code = ((int)response.StatusCode).ToString(),
msg = $"HTTP错误: {response.StatusCode}"
};
}
}
catch (Exception ex)
{
return new ApiResponse<object>
{
code = "500",
msg = $"异常: {ex.Message}"
};
}
}
}
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 第4步: 使用示例
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
public class UsageExample
{
public static void Main()
{
// 创建API客户端
var apiClient = new MyApiClient("https://api.example.com");
// 1. 登录
var loginResult = apiClient.Login("admin", "123456");
if (loginResult.code == "200")
{
Console.WriteLine($"登录成功,Token: {loginResult.data.access_token}");
// 2. 获取用户列表
var userListResult = apiClient.GetUserList();
if (userListResult.code == "200")
{
Console.WriteLine($"用户数量: {userListResult.data.Count}");
foreach (var user in userListResult.data)
{
Console.WriteLine($"- {user.userName} ({user.email})");
}
}
// 3. 创建用户
var newUser = new UserInfo
{
userName = "新用户",
email = "new@example.com"
};
var createResult = apiClient.CreateUser(newUser);
if (createResult.code == "200")
{
Console.WriteLine("用户创建成功");
}
}
else
{
Console.WriteLine($"登录失败: {loginResult.msg}");
}
}
}
}
6.2 直接复制可用的HttpClient版本
csharp
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
public class SimpleWebApiClient
{
private readonly HttpClient _httpClient;
private string _token;
public SimpleWebApiClient(string baseUrl)
{
_httpClient = new HttpClient
{
BaseAddress = new Uri(baseUrl),
Timeout = TimeSpan.FromSeconds(30)
};
}
/// <summary>
/// 登录获取Token
/// </summary>
public async Task<bool> LoginAsync(string username, string password)
{
var loginData = new
{
username = username,
password = password,
grant_type = "password"
};
string json = JsonConvert.SerializeObject(loginData);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("/api/auth/login", content);
string responseJson = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
dynamic result = JsonConvert.DeserializeObject(responseJson);
_token = result.data.access_token;
return true;
}
return false;
}
/// <summary>
/// 发送带Token的POST请求
/// </summary>
public async Task<string> PostWithTokenAsync<T>(string url, T data)
{
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_token}");
string json = JsonConvert.SerializeObject(data);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(url, content);
return await response.Content.ReadAsStringAsync();
}
/// <summary>
/// 发送带Token的GET请求
/// </summary>
public async Task<string> GetWithTokenAsync(string url)
{
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_token}");
var response = await _httpClient.GetAsync(url);
return await response.Content.ReadAsStringAsync();
}
}
7. 常见问题与解决方案
7.1 问题:401 Unauthorized
原因: Token无效或过期
解决方案:
1. 检查Token是否正确设置
2. 检查Token格式: "Bearer " + token (注意Bearer后有空格)
3. 重新登录获取新Token
代码示例:
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
// 重新登录
await LoginAsync();
// 重试请求
response = await PostWithTokenAsync(url, data);
}
7.2 问题:请求超时
原因: 网络延迟或服务器响应慢
解决方案:
1. 增加超时时间
client.Timeout = 30000; // 30秒
2. 使用异步等待
await Task.Delay(1000); // 等待1秒后重试
3. 添加重试机制
int retryCount = 3;
for (int i = 0; i < retryCount; i++)
{
try
{
var response = await client.ExecuteAsync(request);
if (response.IsSuccessful) break;
}
catch { await Task.Delay(1000); }
}
7.3 问题:JSON解析错误
原因: 返回的JSON格式不正确或模型定义不匹配
解决方案:
1. 先打印原始响应查看格式
Console.WriteLine(response.Content);
2. 使用在线工具验证JSON格式
https://jsonlint.com/
3. 检查模型属性名是否匹配(区分大小写)
4. 使用JsonProperty特性映射
[JsonProperty("user_id")] // JSON中的字段名
public string UserId { get; set; } // C#中的属性名
7.4 问题:SSL证书错误
原因: HTTPS证书验证失败
解决方案(仅开发环境):
// 跳过SSL验证
ServicePointManager.ServerCertificateValidationCallback =
(sender, certificate, chain, sslPolicyErrors) => true;
// 设置安全协议
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
7.5 问题:编码乱码
原因: 字符编码不一致
解决方案:
// 确保使用UTF-8编码
var content = new StringContent(
json,
Encoding.UTF8,
"application/json"
);
// 设置请求头
request.AddHeader("Accept-Charset", "UTF-8");
附录:快速参考
A.1 HTTP状态码速查
| 状态码 | 含义 | 处理建议 |
|---|---|---|
| 200 | 成功 | 正常处理数据 |
| 201 | 已创建 | 获取返回的资源ID |
| 400 | 请求错误 | 检查请求参数 |
| 401 | 未认证 | 重新登录获取Token |
| 403 | 禁止访问 | 检查用户权限 |
| 404 | 未找到 | 检查API地址 |
| 500 | 服务器错误 | 联系服务端人员 |
A.2 RestSharp常用方法
csharp
// 创建请求
var request = new RestRequest(Method.POST);
// 添加请求头
request.AddHeader("key", "value");
// 添加JSON请求体
request.AddJsonBody(obj);
// 添加URL参数
request.AddParameter("name", "value", ParameterType.QueryString);
// 添加文件
request.AddFile("file", filePath);
// 执行请求
IRestResponse response = client.Execute(request);
// 异步执行
IRestResponse response = await client.ExecuteAsync(request);
A.3 项目配置
xml
<!-- App.config -->
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
</startup>
<appSettings>
<!-- API服务器地址 -->
<add key="ApiBaseUrl" value="https://api.example.com"/>
<!-- 请求超时时间(毫秒) -->
<add key="RequestTimeout" value="30000"/>
</appSettings>
</configuration>
csharp
// 读取配置
string baseUrl = ConfigurationManager.AppSettings["ApiBaseUrl"];
int timeout = int.Parse(ConfigurationManager.AppSettings["RequestTimeout"]);
文档版本 : 1.0
更新日期 : 2026-04-06
适用框架 : .NET Framework 4.6+ / .NET Core
主要依赖: RestSharp 106.15.0, Newtonsoft.Json 13.0.1