WebApi 通讯-自写Demo技术文档

本文档基于自写的 WebApi 项目编写,讲解 WebApi 通讯的原理、及每个方法的实现细节。

本文只用于对该项目的技术分析。项目为自用项目,源码不对外开源。

目录

  1. 项目概述
  2. [WebApi 通信原理](#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数据
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

相关推荐
唐青枫3 小时前
C#.NET TPL Dataflow 深入解析:数据流管道、背压控制与实战取舍
c#·.net
喵叔哟3 小时前
4.【.NET10 实战--孢子记账--产品智能化】--C# 14 新语法特性详解与实战应用
java·c#·.net
Khsc434ka3 小时前
.NET 10 与智能体时代的架构演进:以 File-Based Apps 为核心的 C# 生态重塑
架构·c#·.net
jackylzh4 小时前
C# 中 LINQ 和 Lambda 表达式的 基本用法
c#
lzhdim4 小时前
C#中加载图片的资源释放
开发语言·c#
山檐雾1 天前
OctreeNode
unity·c#·八叉树
QfC92C02p1 天前
C# 中的 Span 和内存:.NET 中的高性能内存处理
java·c#·.net
Yuri X-20211 天前
VS2022实战测试题——2
程序人生·c#·个人开发·visual studio