界面功能介绍
应用程序主界面包含以下几个主要部分:
-
URL 输入框:用于输入 HTTPS 请求的目标 URL
-
方法选择下拉框:支持选择 GET、POST、PUT、DELETE 四种 HTTP 方法
-
请求体输入框:用于输入 POST/PUT 请求的 JSON 数据
-
发送请求按钮:点击触发请求发送,请求过程中按钮会被禁用
-
响应显示区域:显示请求结果,包括状态码、响应时间和响应内容
-
加载状态条:请求处理过程中显示,指示请求正在进行
使用示例
1. GET 请求示例
功能:获取指定资源的数据
操作步骤:
-
在 URL 输入框中输入:
https://jsonplaceholder.typicode.com/posts/1 -
从方法下拉框中选择:
GET -
请求体输入框留空(GET 请求不需要请求体)
-
点击「发送请求」按钮
预期响应:
状态码: 200 OK
响应时间: 2025-12-01 16:30:00
响应内容:
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

2. POST 请求示例
功能:创建新资源
操作步骤:
-
在 URL 输入框中输入:
https://jsonplaceholder.typicode.com/posts -
从方法下拉框中选择:
POST -
在请求体输入框中输入 JSON 数据:
{ "title": "测试标题", "body": "测试内容", "userId": 1 } -
点击「发送请求」按钮
预期响应:
状态码: 201 Created
响应时间: 2025-12-01 16:31:00
响应内容:
{
"title": "测试标题",
"body": "测试内容",
"userId": 1,
"id": 101
}
3. PUT 请求示例
功能:更新指定资源
操作步骤:
-
在 URL 输入框中输入:
https://jsonplaceholder.typicode.com/posts/1 -
从方法下拉框中选择:
PUT -
在请求体输入框中输入更新后的 JSON 数据:
{ "id": 1, "title": "更新后的标题", "body": "更新后的内容", "userId": 1 } -
点击「发送请求」按钮
预期响应:
状态码: 200 OK
响应时间: 2025-12-01 16:32:00
响应内容:
{
"id": 1,
"title": "更新后的标题",
"body": "更新后的内容",
"userId": 1
}

4. DELETE 请求示例
功能:删除指定资源
操作步骤:
-
在 URL 输入框中输入:
https://jsonplaceholder.typicode.com/posts/1 -
从方法下拉框中选择:
DELETE -
请求体输入框留空(DELETE 请求不需要请求体)
-
点击「发送请求」按钮
预期响应:
状态码: 200 OK
响应时间: 2025-12-01 16:33:00
响应内容:
{}

注意事项
-
HTTPS 证书验证:应用程序使用默认的 HttpClient 证书验证机制,如果目标服务器的 HTTPS 证书无效,会抛出证书验证错误
-
跨域请求:该示例使用的是公共 API(jsonplaceholder.typicode.com),支持跨域请求。如果测试其他 API,可能需要处理跨域问题
-
异步处理:所有请求都是异步处理的,不会阻塞 UI 线程
-
异常处理:应用程序会捕获并显示各种异常,包括网络错误、超时错误等
-
请求超时:HttpClient 默认超时时间为 100 秒,可以在代码中修改
扩展建议
-
添加请求头设置:可以扩展界面,允许用户自定义请求头
-
支持更多 HTTP 方法:可以添加 PATCH、OPTIONS 等其他 HTTP 方法
-
添加响应格式化:可以对 JSON 响应进行格式化,提高可读性
-
添加历史记录功能:可以保存之前的请求历史,方便重复使用
-
添加 SSL/TLS 版本选择:可以允许用户选择不同的 SSL/TLS 版本
技术栈
-
开发框架:.NET Framework 4.7.2
-
UI 框架:WPF
-
设计模式:MVVM
-
HTTP 客户端:HttpClient
-
异步编程:async/await
项目结构
HttpsDemoApp/
├── Model/
│ └── HttpRequestModel.cs # 请求和响应数据模型
├── ViewModel/
│ └── MainViewModel.cs # 业务逻辑和命令绑定
├── View/
├── BoolToInverseBoolConverter.cs # 布尔值反转转换器
├── MainWindow.xaml # 主界面
└── App.xaml # 应用程序入口
运行环境
-
Windows 7 及以上版本
-
.NET Framework 4.7.2 或更高版本
-
Visual Studio 2017 及以上版本(用于开发和编译)
编译和运行
-
使用 Visual Studio 打开
HttpsDemoApp.csproj文件 -
选择
Debug或Release配置 -
点击「生成」->「生成解决方案」
-
点击「调试」->「开始执行(不调试)」或按
Ctrl+F5运行应用程序
或者使用命令行编译和运行:
dotnet build
dotnet run
完整代码:
1. 模型层(Model)- 数据实体定义
using System;
using System.Collections.Generic;
namespace HttpsDemoApp.Model
{
/// <summary>
/// HTTP请求模型
/// 封装请求的核心参数:URL、请求方法、请求体、请求头
/// </summary>
public class HttpRequestModel
{
/// <summary>
/// 请求地址
/// </summary>
public string Url { get; set; }
/// <summary>
/// HTTP请求方法(GET/POST/PUT/DELETE等)
/// </summary>
public string Method { get; set; }
/// <summary>
/// 请求体内容(主要用于POST/PUT请求)
/// </summary>
public string RequestBody { get; set; }
/// <summary>
/// 请求头集合
/// </summary>
public Dictionary<string, string> Headers { get; set; }
/// <summary>
/// 构造函数
/// 初始化请求头字典,默认请求方法为GET
/// </summary>
public HttpRequestModel()
{
Headers = new Dictionary<string, string>();
Method = "GET";
}
}
/// <summary>
/// HTTP响应模型
/// 封装响应的核心参数:状态码、状态信息、响应内容、响应头、响应时间
/// </summary>
public class HttpResponseModel
{
/// <summary>
/// HTTP状态码(200/404/500等)
/// </summary>
public int StatusCode { get; set; }
/// <summary>
/// 状态描述信息(OK/Not Found等)
/// </summary>
public string StatusMessage { get; set; }
/// <summary>
/// 响应内容(JSON/HTML等)
/// </summary>
public string Content { get; set; }
/// <summary>
/// 响应头集合
/// </summary>
public Dictionary<string, string> Headers { get; set; }
/// <summary>
/// 响应接收时间
/// </summary>
public DateTime ResponseTime { get; set; }
/// <summary>
/// 构造函数
/// 初始化响应头字典,默认响应时间为当前时间
/// </summary>
public HttpResponseModel()
{
Headers = new Dictionary<string, string>();
ResponseTime = DateTime.Now;
}
}
}
2. 视图模型层(ViewModel)- 业务逻辑处理(MVVM 核心)
using HttpsDemoApp.Model;
using System;
using System.ComponentModel;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace HttpsDemoApp.ViewModel
{
/// <summary>
/// 主窗口视图模型
/// 实现INotifyPropertyChanged接口,支持属性变更通知(MVVM数据绑定核心)
/// 封装界面交互逻辑和HTTP请求处理逻辑
/// </summary>
public class MainViewModel : INotifyPropertyChanged
{
#region 私有字段
private string _url; // URL输入框绑定字段
private string _method; // 请求方法下拉框绑定字段
private string _requestBody; // 请求体输入框绑定字段
private string _responseContent; // 响应内容显示框绑定字段
private bool _isLoading; // 加载状态标识(控制进度条显示)
private HttpResponseModel _response; // 完整响应数据模型
#endregion
#region 事件定义
/// <summary>
/// 属性变更通知事件
/// 界面绑定的属性变更时触发,通知UI更新
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region 公开属性(绑定到UI)
/// <summary>
/// 请求URL
/// 属性变更时触发PropertyChanged事件
/// </summary>
public string Url
{
get { return _url; }
set { _url = value; OnPropertyChanged(nameof(Url)); }
}
/// <summary>
/// HTTP请求方法
/// </summary>
public string Method
{
get { return _method; }
set { _method = value; OnPropertyChanged(nameof(Method)); }
}
/// <summary>
/// 请求体内容
/// </summary>
public string RequestBody
{
get { return _requestBody; }
set { _requestBody = value; OnPropertyChanged(nameof(RequestBody)); }
}
/// <summary>
/// 响应内容(格式化显示)
/// </summary>
public string ResponseContent
{
get { return _responseContent; }
set { _responseContent = value; OnPropertyChanged(nameof(ResponseContent)); }
}
/// <summary>
/// 加载状态(true=请求中,false=请求完成/未开始)
/// 控制进度条显示和按钮禁用状态
/// </summary>
public bool IsLoading
{
get { return _isLoading; }
set { _isLoading = value; OnPropertyChanged(nameof(IsLoading)); }
}
/// <summary>
/// 完整的响应数据模型
/// 存储原始响应数据,便于扩展使用
/// </summary>
public HttpResponseModel Response
{
get { return _response; }
set { _response = value; OnPropertyChanged(nameof(Response)); }
}
/// <summary>
/// 发送请求命令
/// 绑定到界面的"发送请求"按钮
/// </summary>
public ICommand SendRequestCommand { get; private set; }
#endregion
#region 构造函数
/// <summary>
/// 视图模型初始化
/// 设置默认值,初始化命令
/// </summary>
public MainViewModel()
{
// 设置默认值
Url = "https://jsonplaceholder.typicode.com/posts/1"; // 测试用默认URL
Method = "GET"; // 默认请求方法
RequestBody = ""; // 默认请求体为空
ResponseContent = ""; // 默认响应内容为空
IsLoading = false; // 默认未加载
// 初始化发送请求命令,绑定异步执行方法
SendRequestCommand = new RelayCommand(async param => await SendRequestAsync());
}
#endregion
#region 核心方法
/// <summary>
/// 属性变更通知方法
/// 触发PropertyChanged事件,通知UI更新绑定的属性
/// </summary>
/// <param name="propertyName">变更的属性名</param>
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// 异步发送HTTP请求核心方法
/// 处理不同请求方法(GET/POST/PUT/DELETE),封装响应数据,异常处理
/// </summary>
/// <returns>异步任务</returns>
private async Task SendRequestAsync()
{
// 请求开始:设置加载状态,更新响应提示
IsLoading = true;
ResponseContent = "正在发送请求...";
try
{
// 使用HttpClient发送HTTP请求(using自动释放资源)
using (var httpClient = new HttpClient())
{
HttpResponseMessage response = null;
// 根据请求方法执行不同的HTTP操作
switch (Method.ToUpper())
{
case "GET":
response = await httpClient.GetAsync(Url);
break;
case "POST":
// 构造JSON格式的请求体
var content = new StringContent(RequestBody, Encoding.UTF8, "application/json");
response = await httpClient.PostAsync(Url, content);
break;
case "PUT":
var putContent = new StringContent(RequestBody, Encoding.UTF8, "application/json");
response = await httpClient.PutAsync(Url, putContent);
break;
case "DELETE":
response = await httpClient.DeleteAsync(Url);
break;
}
// 如果响应不为空,封装响应数据到模型
if (response != null)
{
var responseModel = new HttpResponseModel
{
StatusCode = (int)response.StatusCode, // 转换状态码为int
StatusMessage = response.ReasonPhrase, // 状态描述
Content = await response.Content.ReadAsStringAsync(), // 读取响应内容
ResponseTime = DateTime.Now // 记录响应时间
};
// 解析响应头到字典
foreach (var header in response.Headers)
{
responseModel.Headers.Add(header.Key, string.Join(", ", header.Value));
}
// 更新响应模型和格式化显示响应内容
Response = responseModel;
ResponseContent = $"状态码: {responseModel.StatusCode} {responseModel.StatusMessage}\n\n" +
$"响应时间: {responseModel.ResponseTime}\n\n" +
$"响应内容:\n{responseModel.Content}";
}
}
}
catch (HttpRequestException ex)
{
// HTTP请求特定异常处理(如网络错误、URL无效等)
ResponseContent = $"HTTP 请求异常: {ex.Message}\n\n" +
$"Inner Exception: {ex.InnerException?.Message}";
}
catch (Exception ex)
{
// 通用异常处理
ResponseContent = $"异常: {ex.Message}\n\n" +
$"Stack Trace: {ex.StackTrace}";
}
finally
{
// 请求结束:重置加载状态(无论成功/失败都会执行)
IsLoading = false;
}
}
#endregion
}
/// <summary>
/// 异步命令实现类
/// 实现ICommand接口,支持异步执行的RelayCommand(MVVM命令绑定核心)
/// 用于将界面按钮点击事件绑定到视图模型的异步方法
/// </summary>
public class RelayCommand : ICommand
{
private readonly Func<object, Task> _execute; // 异步执行逻辑
private readonly Func<object, bool> _canExecute; // 命令是否可执行的判断逻辑
/// <summary>
/// 命令可执行状态变更事件
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="execute">异步执行方法</param>
/// <param name="canExecute">可执行判断方法(可选)</param>
/// <exception cref="ArgumentNullException">执行方法为空时抛出</exception>
public RelayCommand(Func<object, Task> execute, Func<object, bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
/// <summary>
/// 判断命令是否可执行
/// </summary>
/// <param name="parameter">命令参数</param>
/// <returns>true=可执行,false=不可执行</returns>
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
/// <summary>
/// 执行命令(异步)
/// </summary>
/// <param name="parameter">命令参数</param>
public async void Execute(object parameter)
{
await _execute(parameter);
}
/// <summary>
/// 手动触发可执行状态变更事件
/// 用于更新按钮的可用状态
/// </summary>
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}
3. 转换器(Converter)- UI 值转换
using System;
using System.Globalization;
using System.Windows.Data;
namespace HttpsDemoApp
{
/// <summary>
/// 布尔值反转转换器
/// 将bool值转换为相反值(true→false,false→true)
/// 用于绑定按钮的IsEnabled属性(加载中时禁用按钮)
/// </summary>
public class BoolToInverseBoolConverter : IValueConverter
{
/// <summary>
/// 正向转换(UI→数据源/数据源→UI)
/// </summary>
/// <param name="value">源值(bool类型)</param>
/// <param name="targetType">目标类型</param>
/// <param name="parameter">转换参数</param>
/// <param name="culture">文化信息</param>
/// <returns>反转后的bool值</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return !boolValue;
}
return value;
}
/// <summary>
/// 反向转换(用于双向绑定)
/// </summary>
/// <param name="value">目标值</param>
/// <param name="targetType">源类型</param>
/// <param name="parameter">转换参数</param>
/// <param name="culture">文化信息</param>
/// <returns>反转后的bool值</returns>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return !boolValue;
}
return value;
}
}
}
4. 视图层(View)- 界面布局(XAML)
<Window x:Class="HttpsDemoApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:HttpsDemoApp"
xmlns:vm="clr-namespace:HttpsDemoApp.ViewModel"
mc:Ignorable="d"
Title="WPF HTTPS 通信示例" Height="600" Width="900">
<!-- 资源定义:转换器 -->
<Window.Resources>
<!-- WPF内置布尔值转可见性转换器(true=Visible,false=Collapsed) -->
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<!-- 自定义布尔值反转转换器 -->
<local:BoolToInverseBoolConverter x:Key="BoolToInverseBoolConverter" />
</Window.Resources>
<!-- 数据上下文:绑定到主视图模型 -->
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<!-- 主布局网格 -->
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <!-- URL行 -->
<RowDefinition Height="Auto" /> <!-- 方法+按钮行 -->
<RowDefinition Height="*" /> <!-- 请求体+响应行 -->
<RowDefinition Height="Auto" /> <!-- 加载进度条行 -->
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <!-- 左侧列(请求体) -->
<ColumnDefinition Width="100" /> <!-- 右侧列(响应) -->
</Grid.ColumnDefinitions>
<!-- 1. URL输入区域 -->
<Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <!-- 标签列 -->
<ColumnDefinition Width="*" /> <!-- 输入框列 -->
</Grid.ColumnDefinitions>
<Label Content="URL:" VerticalAlignment="Center" Margin="0,0,10,0" />
<!-- 绑定到ViewModel的Url属性(双向绑定) -->
<TextBox Text="{Binding Url}" Grid.Column="1" Height="30" VerticalContentAlignment="Center" />
</Grid>
<!-- 2. 请求方法选择+发送按钮区域 -->
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <!-- 标签列 -->
<ColumnDefinition Width="100" /> <!-- 下拉框列 -->
<ColumnDefinition Width="*" /> <!-- 空白列(占位) -->
<ColumnDefinition Width="100" /> <!-- 按钮列 -->
</Grid.ColumnDefinitions>
<Label Content="方法:" VerticalAlignment="Center" Margin="0,0,10,0" />
<!-- 请求方法下拉框:绑定到ViewModel的Method属性 -->
<ComboBox SelectedValue="{Binding Method}" SelectedValuePath="Content" Grid.Column="1" Height="30">
<ComboBoxItem Content="GET" />
<ComboBoxItem Content="POST" />
<ComboBoxItem Content="PUT" />
<ComboBoxItem Content="DELETE" />
</ComboBox>
<!-- 发送请求按钮:绑定命令,加载中时禁用 -->
<Button Content="发送请求" Grid.Column="3" Height="30"
Command="{Binding SendRequestCommand}"
IsEnabled="{Binding IsLoading, Converter={StaticResource BoolToInverseBoolConverter}}" />
</Grid>
<!-- 3. 请求体输入区域(左侧) -->
<Grid Grid.Row="2" Grid.Column="0" Margin="0,0,0,9.667" HorizontalAlignment="Left" Width="402">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <!-- 标签行 -->
<RowDefinition Height="*" /> <!-- 输入框行 -->
</Grid.RowDefinitions>
<Label Content="请求体:" Margin="0,0,0,5" />
<!-- 请求体输入框:支持换行,双向绑定到RequestBody -->
<TextBox Text="{Binding RequestBody}" Grid.Row="1" AcceptsReturn="True"
TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"
Margin="0,0.333,-52,-0.333" />
</Grid>
<!-- 4. 响应内容显示区域(右侧) -->
<Grid Grid.Row="2" Grid.Column="1" Margin="0,0,0,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <!-- 标签行 -->
<RowDefinition Height="20*" /> <!-- 响应内容行 -->
<RowDefinition Height="23*"/>
</Grid.RowDefinitions>
<Label Content="响应:" Margin="0,0,0,4.667" />
<!-- 响应内容显示框:只读,绑定到ResponseContent -->
<TextBox Text="{Binding ResponseContent}" Grid.Row="1" IsReadOnly="True"
AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"
Margin="-307,0,0,-0.333" Grid.RowSpan="2" />
</Grid>
<!-- 5. 加载进度条区域 -->
<Grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2">
<!-- 进度条:加载中时显示无限滚动动画,绑定IsLoading -->
<ProgressBar IsIndeterminate="{Binding IsLoading}" Height="10"
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibilityConverter}}"></ProgressBar>
</Grid>
</Grid>
</Window>
框架说明
该项目采用 MVVM(Model-View-ViewModel) 设计模式,核心分层如下:
| 层级 | 作用 | 对应文件 / 类 |
|---|---|---|
| Model(模型) | 封装数据结构,定义 HTTP 请求 / 响应的核心参数,与业务逻辑解耦 | HttpRequestModel、HttpResponseModel |
| View(视图) | 纯界面展示,通过数据绑定与 ViewModel 交互,无业务逻辑 | MainWindow.xaml |
| ViewModel(视图模型) | 连接 View 和 Model 的桥梁,处理业务逻辑(HTTP 请求),实现属性变更通知 | MainViewModel、RelayCommand |
| 转换器 | 处理 UI 绑定中的值转换(如布尔值反转、布尔值转可见性) | BoolToInverseBoolConverter |
核心特性
- 异步请求 :使用
async/await处理 HTTP 请求,避免 UI 阻塞 - 数据绑定 :通过
INotifyPropertyChanged实现属性变更通知,UI 自动更新 - 命令绑定 :使用
RelayCommand将按钮点击事件绑定到 ViewModel 的异步方法 - 异常处理:捕获 HTTP 请求异常和通用异常,友好展示错误信息
- 状态管理 :通过
IsLoading控制加载进度条和按钮状态,提升用户体验
效果展示:
测试 API
示例中使用的是 JSONPlaceholder,这是一个免费的在线 REST API,用于测试和原型开发。你也可以使用自己的 HTTPS API 进行测试。
常见问题
1. 为什么请求失败并显示证书错误?
答:这可能是因为目标服务器的 HTTPS 证书无效或未被信任。你可以在代码中添加证书验证忽略逻辑,但不建议在生产环境中使用。
2. 为什么 POST 请求返回 400 Bad Request?
答:这可能是因为请求体格式不正确。请确保请求体是有效的 JSON 格式,并且符合 API 的要求。
3. 为什么请求超时?
答:这可能是因为网络问题或目标服务器响应缓慢。你可以在代码中修改 HttpClient 的超时时间。
4. 为什么应用程序启动时显示资源引用错误?
答:这可能是因为 XAML 中资源定义的顺序不正确。请确保 Window.Resources 节点在 Window.DataContext 节点之前。
总结
本示例展示了如何使用 WPF 和 HttpClient 实现 HTTPS 通信,采用了 MVVM 设计模式,实现了界面与业务逻辑的分离。示例支持多种 HTTP 方法,具有完整的异常处理和加载状态显示功能,可以作为 WPF HTTPS 通信的基础模板,根据实际需求进行扩展和修改。
