C# WinForm 程序中实现一个简易的 HTTP 服务器,能够处理 JSON 格式的 GET 和 POST 请求,这是一个很实用的需求,尤其适合做本地调试或轻量级服务。
实现思路
我会基于.NET自带的HttpListener类来实现这个 HTTP 服务器,它可以让 WinForm 程序监听指定端口并处理 HTTP 请求。核心步骤包括:
-
1、初始化
HttpListener并配置监听地址 -
2、异步监听并处理客户端请求
-
3、区分 GET/POST 请求类型
-
4、解析 / 响应 JSON 格式数据
-
5、在 WinForm 界面中控制服务器的启动和停止
完整代码实现
以下是一个开箱即用的 WinForm 程序代码,包含 UI 交互和完整的 HTTP 服务逻辑:
- 首先是 Form1 的设计(Form1.Designer.cs 核心部分)
cs
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace HttpServerWinFormClient
{
public partial class Form1 : Form
{
private readonly HttpClient _httpClient;
// 默认服务器地址
private string _baseUrl = "http://localhost:8080/";
public Form1()
{
InitializeComponent();
// 初始化HttpClient(设置超时10秒)
_httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
// 初始化界面
txtServerUrl.Text = _baseUrl;
cbxMethod.SelectedIndex = 0; // 默认GET
// 预设JSON模板
txtRequestJson.Text = @"
{
""Id"": 1001,
""Name"": ""张三-测试中文"",
""Email"": ""zhangsan@test.com""
}";
// 清空响应区
ClearResponse();
}
#region 界面事件
/// <summary>
/// 发送请求按钮点击
/// </summary>
private async void btnSend_Click(object sender, EventArgs e)
{
try
{
// 禁用按钮,防止重复点击
btnSend.Enabled = false;
ClearResponse();
_baseUrl = txtServerUrl.Text.Trim();
if (string.IsNullOrWhiteSpace(_baseUrl))
{
AppendLog("错误:服务器地址不能为空!", LogLevel.Error);
btnSend.Enabled = true;
return;
}
// 记录开始时间
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
AppendLog($"开始发送{cbxMethod.Text}请求到:{_baseUrl}", LogLevel.Info);
HttpResponseMessage response = null;
if (cbxMethod.Text == "GET")
{
// 发送GET请求
response = await _httpClient.GetAsync(_baseUrl);
}
else if (cbxMethod.Text == "POST")
{
// 验证POST请求体
string json = txtRequestJson.Text.Trim();
if (string.IsNullOrWhiteSpace(json))
{
AppendLog("错误:POST请求体不能为空!", LogLevel.Error);
btnSend.Enabled = true;
return;
}
// 构造POST内容(UTF-8编码)
var content = new StringContent(json, Encoding.UTF8, "application/json");
content.Headers.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
// 发送POST请求
response = await _httpClient.PostAsync(_baseUrl, content);
}
// 停止计时
stopwatch.Stop();
// 解析响应
string responseContent = await response.Content.ReadAsStringAsync();
// 显示响应信息
lblStatusCode.Text = $"状态码:{(int)response.StatusCode} ({response.StatusCode})";
lblTimeCost.Text = $"耗时:{stopwatch.ElapsedMilliseconds} ms";
txtResponseJson.Text = FormatJson(responseContent);
// 记录日志
AppendLog($"请求完成 - 状态码:{(int)response.StatusCode},耗时:{stopwatch.ElapsedMilliseconds}ms", LogLevel.Success);
}
catch (HttpRequestException ex)
{
AppendLog($"请求异常:{ex.Message}(请检查服务器是否启动/地址是否正确)", LogLevel.Error);
}
catch (TaskCanceledException)
{
AppendLog("请求超时:服务器无响应(超时10秒)", LogLevel.Error);
}
catch (Exception ex)
{
AppendLog($"未知异常:{ex.Message}", LogLevel.Error);
}
finally
{
btnSend.Enabled = true;
}
}
/// <summary>
/// 清空响应按钮
/// </summary>
private void btnClear_Click(object sender, EventArgs e)
{
ClearResponse();
txtLog.Clear();
}
/// <summary>
/// 快速测试:正常POST(含中文)
/// </summary>
private void btnTestNormalPost_Click(object sender, EventArgs e)
{
cbxMethod.SelectedIndex = 1; // 切换到POST
txtRequestJson.Text = @"
{
""Id"": 1001,
""Name"": ""张三-测试中文"",
""Email"": ""zhangsan@test.com""
}";
AppendLog("已加载【正常POST(含中文)】测试模板", LogLevel.Info);
}
/// <summary>
/// 快速测试:错误JSON(Id为字符串)
/// </summary>
private void btnTestErrorPost_Click(object sender, EventArgs e)
{
cbxMethod.SelectedIndex = 1; // 切换到POST
txtRequestJson.Text = @"
{
""Id"": ""1001a"",
""Name"": ""李四"",
""Email"": ""lisi@test.com""
}";
AppendLog("已加载【错误JSON(Id为字符串)】测试模板", LogLevel.Info);
}
/// <summary>
/// 请求方法切换(隐藏/显示POST请求体)
/// </summary>
private void cbxMethod_SelectedIndexChanged(object sender, EventArgs e)
{
// GET隐藏请求体,POST显示
panelPostJson.Visible = (cbxMethod.Text == "POST");
}
#endregion
#region 辅助方法
/// <summary>
/// 清空响应区域
/// </summary>
private void ClearResponse()
{
lblStatusCode.Text = "状态码:-";
lblTimeCost.Text = "耗时:- ms";
txtResponseJson.Clear();
}
/// <summary>
/// 格式化JSON字符串(便于阅读)
/// </summary>
private string FormatJson(string json)
{
if (string.IsNullOrWhiteSpace(json)) return "";
try
{
// 使用Newtonsoft.Json格式化(需安装NuGet包)
dynamic parsedJson = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
return Newtonsoft.Json.JsonConvert.SerializeObject(parsedJson, Newtonsoft.Json.Formatting.Indented);
}
catch
{
// 格式化失败则返回原字符串
return json;
}
}
/// <summary>
/// 日志级别枚举
/// </summary>
private enum LogLevel
{
Info,
Success,
Error
}
/// <summary>
/// 追加日志(线程安全)
/// </summary>
private void AppendLog(string message, LogLevel level = LogLevel.Info)
{
if (txtLog.InvokeRequired)
{
txtLog.Invoke(new Action<string, LogLevel>(AppendLog), message, level);
return;
}
string timeStr = $"[{DateTime.Now:HH:mm:ss.fff}]";
string levelStr = level switch
{
LogLevel.Info => "[信息]",
LogLevel.Success => "[成功]",
LogLevel.Error => "[错误]",
_ => "[未知]"
};
string logLine = $"{timeStr} {levelStr} {message}{Environment.NewLine}";
txtLog.AppendText(logLine);
// 自动滚动到最后一行
txtLog.SelectionStart = txtLog.TextLength;
txtLog.ScrollToCaret();
}
#endregion
#region 窗体设计器代码(自动生成)
private System.ComponentModel.IContainer components = null;
private TextBox txtServerUrl;
private Label label1;
private ComboBox cbxMethod;
private Button btnSend;
private Panel panelPostJson;
private Label label2;
private TextBox txtRequestJson;
private Label label3;
private TextBox txtResponseJson;
private Label lblStatusCode;
private Label lblTimeCost;
private Button btnClear;
private GroupBox groupBox1;
private GroupBox groupBox2;
private GroupBox groupBox3;
private TextBox txtLog;
private Button btnTestNormalPost;
private Button btnTestErrorPost;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
_httpClient?.Dispose();
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.txtServerUrl = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.cbxMethod = new System.Windows.Forms.ComboBox();
this.btnSend = new System.Windows.Forms.Button();
this.panelPostJson = new System.Windows.Forms.Panel();
this.txtRequestJson = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.txtResponseJson = new System.Windows.Forms.TextBox();
this.lblStatusCode = new System.Windows.Forms.Label();
this.lblTimeCost = new System.Windows.Forms.Label();
this.btnClear = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.btnTestErrorPost = new System.Windows.Forms.Button();
this.btnTestNormalPost = new System.Windows.Forms.Button();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.txtLog = new System.Windows.Forms.TextBox();
this.panelPostJson.SuspendLayout();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.groupBox3.SuspendLayout();
this.SuspendLayout();
// txtServerUrl
this.txtServerUrl.Location = new System.Drawing.Point(85, 15);
this.txtServerUrl.Name = "txtServerUrl";
this.txtServerUrl.Size = new System.Drawing.Size(250, 23);
this.txtServerUrl.TabIndex = 0;
// label1
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(10, 18);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(69, 17);
this.label1.TabIndex = 1;
this.label1.Text = "服务器地址:";
// cbxMethod
this.cbxMethod.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbxMethod.Items.AddRange(new object[] { "GET", "POST" });
this.cbxMethod.Location = new System.Drawing.Point(341, 15);
this.cbxMethod.Name = "cbxMethod";
this.cbxMethod.Size = new System.Drawing.Size(80, 25);
this.cbxMethod.TabIndex = 2;
this.cbxMethod.SelectedIndexChanged += new System.EventHandler(this.cbxMethod_SelectedIndexChanged);
// btnSend
this.btnSend.Location = new System.Drawing.Point(427, 15);
this.btnSend.Name = "btnSend";
this.btnSend.Size = new System.Drawing.Size(80, 25);
this.btnSend.TabIndex = 3;
this.btnSend.Text = "发送请求";
this.btnSend.UseVisualStyleBackColor = true;
this.btnSend.Click += new System.EventHandler(this.btnSend_Click);
// panelPostJson
this.panelPostJson.Controls.Add(this.txtRequestJson);
this.panelPostJson.Controls.Add(this.label2);
this.panelPostJson.Location = new System.Drawing.Point(10, 45);
this.panelPostJson.Name = "panelPostJson";
this.panelPostJson.Size = new System.Drawing.Size(497, 120);
this.panelPostJson.TabIndex = 4;
// txtRequestJson
this.txtRequestJson.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtRequestJson.Multiline = true;
this.txtRequestJson.Name = "txtRequestJson";
this.txtRequestJson.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtRequestJson.Size = new System.Drawing.Size(497, 95);
this.txtRequestJson.TabIndex = 1;
// label2
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(0, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(79, 17);
this.label2.TabIndex = 0;
this.label2.Text = "POST请求体:";
// label3
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(10, 170);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(79, 17);
this.label3.TabIndex = 5;
this.label3.Text = "响应内容:";
// txtResponseJson
this.txtResponseJson.Location = new System.Drawing.Point(10, 190);
this.txtResponseJson.Multiline = true;
this.txtResponseJson.Name = "txtResponseJson";
this.txtResponseJson.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtResponseJson.Size = new System.Drawing.Size(497, 120);
this.txtResponseJson.TabIndex = 6;
// lblStatusCode
this.lblStatusCode.AutoSize = true;
this.lblStatusCode.Location = new System.Drawing.Point(10, 315);
this.lblStatusCode.Name = "lblStatusCode";
this.lblStatusCode.Size = new System.Drawing.Size(53, 17);
this.lblStatusCode.TabIndex = 7;
this.lblStatusCode.Text = "状态码:-";
// lblTimeCost
this.lblTimeCost.AutoSize = true;
this.lblTimeCost.Location = new System.Drawing.Point(120, 315);
this.lblTimeCost.Name = "lblTimeCost";
this.lblTimeCost.Size = new System.Drawing.Size(65, 17);
this.lblTimeCost.TabIndex = 8;
this.lblTimeCost.Text = "耗时:- ms";
// btnClear
this.btnClear.Location = new System.Drawing.Point(427, 310);
this.btnClear.Name = "btnClear";
this.btnClear.Size = new System.Drawing.Size(80, 25);
this.btnClear.TabIndex = 9;
this.btnClear.Text = "清空";
this.btnClear.UseVisualStyleBackColor = true;
this.btnClear.Click += new System.EventHandler(this.btnClear_Click);
// groupBox1
this.groupBox1.Controls.Add(this.btnTestErrorPost);
this.groupBox1.Controls.Add(this.btnTestNormalPost);
this.groupBox1.Controls.Add(this.txtServerUrl);
this.groupBox1.Controls.Add(this.label1);
this.groupBox1.Controls.Add(this.cbxMethod);
this.groupBox1.Controls.Add(this.btnSend);
this.groupBox1.Controls.Add(this.panelPostJson);
this.groupBox1.Location = new System.Drawing.Point(10, 10);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(517, 180);
this.groupBox1.TabIndex = 10;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "请求配置";
// btnTestNormalPost
this.btnTestNormalPost.Location = new System.Drawing.Point(10, 170);
this.btnTestNormalPost.Name = "btnTestNormalPost";
this.btnTestNormalPost.Size = new System.Drawing.Size(150, 25);
this.btnTestNormalPost.TabIndex = 5;
this.btnTestNormalPost.Text = "快速测试:正常POST(中文)";
this.btnTestNormalPost.UseVisualStyleBackColor = true;
this.btnTestNormalPost.Click += new System.EventHandler(this.btnTestNormalPost_Click);
// btnTestErrorPost
this.btnTestErrorPost.Location = new System.Drawing.Point(166, 170);
this.btnTestErrorPost.Name = "btnTestErrorPost";
this.btnTestErrorPost.Size = new System.Drawing.Size(150, 25);
this.btnTestErrorPost.TabIndex = 6;
this.btnTestErrorPost.Text = "快速测试:错误JSON(Id字符串)";
this.btnTestErrorPost.UseVisualStyleBackColor = true;
this.btnTestErrorPost.Click += new System.EventHandler(this.btnTestErrorPost_Click);
// groupBox2
this.groupBox2.Controls.Add(this.label3);
this.groupBox2.Controls.Add(this.txtResponseJson);
this.groupBox2.Controls.Add(this.lblStatusCode);
this.groupBox2.Controls.Add(this.lblTimeCost);
this.groupBox2.Controls.Add(this.btnClear);
this.groupBox2.Location = new System.Drawing.Point(10, 195);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(517, 340);
this.groupBox2.TabIndex = 11;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "响应结果";
// groupBox3
this.groupBox3.Controls.Add(this.txtLog);
this.groupBox3.Location = new System.Drawing.Point(10, 540);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(517, 150);
this.groupBox3.TabIndex = 12;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "操作日志";
// txtLog
this.txtLog.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtLog.Multiline = true;
this.txtLog.Name = "txtLog";
this.txtLog.ReadOnly = true;
this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtLog.Size = new System.Drawing.Size(517, 125);
this.txtLog.TabIndex = 0;
// Form1
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(539, 700);
this.Controls.Add(this.groupBox3);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Name = "Form1";
this.Text = "HTTP服务器测试客户端";
this.panelPostJson.ResumeLayout(false);
this.panelPostJson.PerformLayout();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.groupBox3.ResumeLayout(false);
this.groupBox3.PerformLayout();
this.ResumeLayout(false);
}
#endregion
}
}


APIPost软件测试:

关键代码解释
-
1、HttpListener
:核心类,用于监听 HTTP 请求,支持多前缀、多请求处理
-
2、多线程处理
:监听线程和请求处理线程分离,避免阻塞 UI 线程
-
3、JSON 序列化 / 反序列化
:使用
JavaScriptSerializer处理 JSON 数据(也可以替换为 Newtonsoft.Json,更推荐) -
4、跨线程 UI 更新
:通过
Control.Invoke实现日志的跨线程输出,避免 WinForm 跨线程访问异常 -
5、异常处理
:完善的异常捕获和错误响应,保证服务器稳定运行
总结
-
1、该 WinForm 服务器基于
HttpListener实现,能够稳定处理 JSON 格式的 GET/POST 请求,核心是异步监听 + 多线程处理请求,避免阻塞 UI。 -
2、关键要点:需要管理员权限运行、正确处理跨线程 UI 更新、严格遵循 HTTP 响应规范(设置 Content-Type、关闭响应流)。