本文档基于自写的 WebApi 项目编写,讲解 WebApi 通讯的原理、及每个方法的实现细节。
本文只用于对该项目的技术分析。项目为自用项目,源码不对外开源。

目录

  1. 项目概述
  2. WebApi 通信原理
  3. 核心技术详解
  4. 代码逐行解析
  5. 五种调用方法详解
  6. 实战代码模板
  7. 常见问题与解决方案

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数据
/// <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请求:

POST /api-xxx HTTP/1.1
Host: www.xxx.com
Content-Type: application/json
Content-Length: 45

{"keyId":"xxx","keySecret":"yyy"}

方法二:HttpPostBearerToken - 带Token的POST请求
/// <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请求:

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 - 表单数据提交
/// <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请求:

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请求
/// <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请求:

GET /xxx/xxx/xxx?id=123 HTTP/1.1
Host: www.xxx.com

4.2 DeviceApi.cs 完整解析

这是业务API封装类,包含所有业务接口调用。

构造函数 - API地址初始化
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
/// <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);
    }
}

响应数据结构:

{
  "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 - 获取项目列表
/// <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 - 读取变量值
/// <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 - 写入变量值
/// <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 完整解析

主窗体业务逻辑代码。

登录按钮事件
/// <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, "登陆失败");
    }
}

读取变量按钮事件
/// <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提交

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 定义请求数据模型
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
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提交

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 调用示例
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
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 - 表单提交

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 调用示例
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
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请求

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 调用示例
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
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调用流程模板

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版本

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常用方法

// 创建请求
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 项目配置

<!-- 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>
// 读取配置
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

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐