You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core控制器向WinForms客户端共享复杂对象的最优方案

问题描述

当ASP.NET Core控制器需要向WinForms客户端返回复杂对象时,如何让其他开发者明确对象结构,以正确调用服务端接口并完成对象转换?

服务端控制器代码

using GaiaProjectSoftwareDevelopmentExercise.Services;
using Microsoft.AspNetCore.Mvc;

namespace GaiaProjectSoftwareDevelopmentExercise.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class OperatorsController : ControllerBase
    {
        private readonly NumbersOperationService numbersOperationService;
        private DataBaseManager dataBaseManager = new DataBaseManager();

        public OperatorsController()
        {
            this.numbersOperationService = new NumbersOperationService();
        }

        [HttpGet("[action]")]
        public IActionResult GetListOfOperators()
        {
            try
            {
                return Ok(new List<string>() 
                {   "AddNumbers", 
                    "SubtractNumbers",
                    "MultiplyNumbers",
                    "DivideNumbers",
                    "ConcatenateStrings" });
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [HttpGet("[action]")]
        public IActionResult AddNumbers(double first, double second)
        {
            try
            {
                double result = numbersOperationService.Add(first, second);
                dataBaseManager.addValues("AddNumbers", first.ToString(), second.ToString(), result.ToString());
                return Ok(result);
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [HttpGet("[action]")]
        public IActionResult SubtractNumbers(double first, double second)
        {
            try
            {
                double result = numbersOperationService.Subtract(first, second);
                dataBaseManager.addValues("SubtractNumbers", first.ToString(), second.ToString(), result.ToString());
                return Ok(result);
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [HttpGet("[action]")]
        public IActionResult MultiplyNumbers(double first, double second)
        {
            try
            {
                double result = numbersOperationService.Multiply(first, second);
                dataBaseManager.addValues("MultiplyNumbers", first.ToString(), second.ToString(), result.ToString());
                return Ok(result);
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [HttpGet("[action]")]
        public IActionResult DivideNumbers(double first, double second)
        {
            try
            {
                double result = numbersOperationService.Divide(first, second);
                dataBaseManager.addValues("DivideNumbers", first.ToString(), second.ToString(), result.ToString());
                return Ok(result);
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [HttpGet("[action]")]
        public IActionResult ConcatenateStrings(string first, string second)
        {
            try
            {
                string result = string.Concat(first, second);
                dataBaseManager.addValues("ConcatenateStrings", first.ToString(), second.ToString(), result.ToString());
                return Ok(result);
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

    }
}

WinForms客户端代码

using Newtonsoft.Json;

namespace GaiaProjectClientSide
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            LoadOperations();
        }

        private const string apiUrl = "http://localhost:5207/Operators/";

        private async void LoadOperations()
        {
            // 从服务器加载操作列表并绑定到下拉框
            using (HttpClient client = new HttpClient())
            {
                try
                {
                    HttpResponseMessage response = await client.GetAsync($"{apiUrl}GetListOfOperators");
                    comboBox1.DataSource = JsonConvert.DeserializeObject<List<string>>(response.Content.ReadAsStringAsync().Result);

                }
                catch (Exception ex)
                {
                    MessageBox.Show($"加载操作列表出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            using (HttpClient client = new HttpClient())
            {
                try
                {
                    string _operator = comboBox1.SelectedValue.ToString();
                    HttpResponseMessage response = client.GetAsync($"{apiUrl}{_operator}?first={textBox1.Text}&second={textBox2.Text}").Result;
                    textBox3.Text = JsonConvert.DeserializeObject<string>(response.Content.ReadAsStringAsync().Result);
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"计算出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
    }
}

最优实现方案

按落地效率和可靠性排序,推荐以下三种方式:

1. 构建共享DTO类库

创建一个独立的类库项目(例如GaiaProject.Shared),把所有接口涉及的复杂对象都定义成强类型的数据传输对象(DTO),服务端Web项目和WinForms客户端项目直接引用这个类库:

  • 服务端控制器直接返回DTO实例,比如return Ok(new OperationResultDto { OperationName = "AddNumbers", NumericResult = result, OperationTime = DateTime.Now });;
  • 客户端反序列化时直接用JsonConvert.DeserializeObject<OperationResultDto>(response.Content.ReadAsStringAsync().Result),完全不用手动猜测对象结构,类型不匹配会直接在编译阶段报错,从根源避免转换错误。

示例DTO类:

public class OperationResultDto
{
    public string OperationName { get; set; }
    public double? NumericResult { get; set; }
    public string StringResult { get; set; }
    public DateTime OperationTime { get; set; }
}

2. 自动生成强类型客户端代理

用Swashbuckle给你的API生成OpenAPI规范文档,再用NSwag工具根据这份文档自动生成WinForms可用的强类型客户端代码:

  • 生成的客户端会把每个API接口封装成对应的方法,返回值直接是匹配的复杂对象类型;
  • 开发者调用接口时只需要传入参数,不用手动拼接URL、处理序列化逻辑,对象结构完全同步服务端定义,零手动转换出错概率。

3. 完善API文档注释

如果暂时不想搞共享类库或自动生成,可以在控制器方法上添加详细的XML注释,明确返回对象的每个字段名称、类型和含义,再配置Swagger把这些注释展示在UI中:

/// <summary>
/// 执行加法操作并返回结果对象
/// </summary>
/// <returns>操作结果对象包含以下字段:
/// <para>OperationName: 固定为"AddNumbers"的操作名称</para>
/// <para>NumericResult: 加法计算的数值结果</para>
/// <para>OperationTime: 操作执行的时间</para>
/// </returns>
[HttpGet("[action]")]
public IActionResult AddNumbers(double first, double second)
{
    // 业务逻辑
}

其他开发者通过Swagger UI就能清晰查看对象结构,手动编写客户端转换逻辑时有明确的参考依据。

内容的提问来源于stack exchange,提问作者hedbisker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 04:22:05