一、项目整体结构
该项目是一个基于 WPF 的 HTTPS 通信演示程序,采用 MVVM 设计模式,主要实现了 GET/POST/PUT/DELETE 四种 HTTP 请求的发送,并展示响应结果。项目分为三个核心部分:
-
模型层(Model):定义请求和响应的数据结构
-
视图模型层(ViewModel):处理业务逻辑,实现数据绑定和命令绑定
-
视图层(View):MainWindow.xaml,负责 UI 展示
二、模型层(Model)代码分析
1. 功能说明
定义HttpRequestModel(HTTP 请求模型)和HttpResponseModel(HTTP 响应模型),封装请求 / 响应的核心属性,提供默认构造函数初始化默认值。
2. 核心代码
using System;
using System.Collections.Generic;
namespace HttpsDemoApp.Model
{
// HTTP请求模型:封装请求的URL、方法、请求体、请求头
public class HttpRequestModel
{
public string Url { get; set; }
public string Method { get; set; }
public string RequestBody { get; set; }
public Dictionary<string, string> Headers { get; set; }
// 构造函数:初始化请求头字典,默认请求方法为GET
public HttpRequestModel()
{
Headers = new Dictionary<string, string>();
Method = "GET";
}
}
// HTTP响应模型:封装响应的状态码、状态信息、响应内容、响应头、响应时间
public class HttpResponseModel
{
public int StatusCode { get; set; }
public string StatusMessage { get; set; }
public string Content { get; set; }
public Dictionary<string, string> Headers { get; set; }
public DateTime ResponseTime { get; set; }
// 构造函数:初始化响应头字典,默认响应时间为当前时间
public HttpResponseModel()
{
Headers = new Dictionary<string, string>();
ResponseTime = DateTime.Now;
}
}
}
3. 关键要点
-
HttpRequestModel:默认方法为 GET,请求头初始化为空字典 -
HttpResponseModel:响应时间默认取当前时间,响应头初始化为空字典 -
采用
Dictionary<string, string>存储请求 / 响应头,便于键值对管理
三、视图模型层(ViewModel)代码分析
1. 功能说明
实现MainViewModel(核心视图模型)和RelayCommand(命令绑定实现),处理 HTTP 请求发送逻辑,实现INotifyPropertyChanged接口支持数据双向绑定。
2. 核心代码(MainViewModel)
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
{
// 主视图模型:实现INotifyPropertyChanged支持属性变更通知
public class MainViewModel : INotifyPropertyChanged
{
// 私有字段
private string _url;
private string _method;
private string _requestBody;
private string _responseContent;
private bool _isLoading;
private HttpResponseModel _response;
// 属性变更事件
public event PropertyChangedEventHandler PropertyChanged;
// 公开属性(带变更通知)
public string Url
{
get { return _url; }
set { _url = value; OnPropertyChanged(nameof(Url)); }
}
public string Method
{
get { return _method; }
set { _method = value; OnPropertyChanged(nameof(Method)); }
}
public string RequestBody
{
get { return _requestBody; }
set { _requestBody = value; OnPropertyChanged(nameof(RequestBody)); }
}
public string ResponseContent
{
get { return _responseContent; }
set { _responseContent = value; OnPropertyChanged(nameof(ResponseContent)); }
}
public bool IsLoading
{
get { return _isLoading; }
set { _isLoading = value; OnPropertyChanged(nameof(IsLoading)); }
}
public HttpResponseModel Response
{
get { return _response; }
set { _response = value; OnPropertyChanged(nameof(Response)); }
}
// 发送请求命令
public ICommand SendRequestCommand { get; private set; }
// 构造函数:初始化默认值,绑定命令
public MainViewModel()
{
Url = "https://jsonplaceholder.typicode.com/posts/1"; // 测试URL
Method = "GET";
RequestBody = "";
ResponseContent = "";
IsLoading = false;
SendRequestCommand = new RelayCommand(async param => await SendRequestAsync());
}
// 属性变更通知方法
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// 异步发送HTTP请求核心方法
private async Task SendRequestAsync()
{
IsLoading = true;
ResponseContent = "正在发送请求...";
try
{
using (var httpClient = new HttpClient())
{
HttpResponseMessage response = null;
// 根据请求方法执行不同操作
switch (Method.ToUpper())
{
case "GET":
response = await httpClient.GetAsync(Url);
break;
case "POST":
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,
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请求异常处理
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; // 无论是否异常,最终结束加载状态
}
}
}
}
3. 核心代码(RelayCommand)
// 命令实现类:支持异步执行的RelayCommand
public class RelayCommand : ICommand
{
private readonly Func<object, Task> _execute;
private readonly Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged;
// 构造函数:初始化执行方法和可执行判断方法
public RelayCommand(Func<object, Task> execute, Func<object, bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
// 判断命令是否可执行
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
// 执行命令(异步)
public async void Execute(object parameter)
{
await _execute(parameter);
}
// 触发可执行状态变更
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
4. 关键要点
(1)MVVM 核心特性
-
实现
INotifyPropertyChanged接口:通过OnPropertyChanged方法触发属性变更通知,使 UI 自动更新 -
命令绑定:通过
RelayCommand将按钮点击事件绑定到SendRequestAsync方法 -
异步处理:使用
async/await处理 HTTP 请求,避免 UI 线程阻塞
(2)HTTP 请求处理
-
支持 GET/POST/PUT/DELETE 四种方法,POST/PUT 默认使用 JSON 格式请求体(UTF8 编码)
-
使用
HttpClient发送请求,通过using语句自动释放资源 -
异常处理:区分
HttpRequestException(HTTP 相关异常)和通用异常,友好展示错误信息
(3)状态管理
-
IsLoading:控制加载进度条显示,请求发送时设为 true,结束后设为 false -
响应结果格式化:将状态码、响应时间、响应内容拼接展示在
ResponseContent中
四、视图层(View)代码分析
1. 功能说明
MainWindow.xaml 作为 UI 界面,通过数据绑定关联 ViewModel 的属性和命令,实现用户交互和结果展示。
2. 核心代码
<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>
<!-- 布尔值转可见性转换器 -->
<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" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<!-- 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" />
<TextBox Text="{Binding Url}" Grid.Column="1" Height="30" VerticalContentAlignment="Center" />
</Grid>
<!-- 请求方法选择和发送按钮 -->
<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" />
<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>
<!-- 请求体输入框 -->
<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" />
<TextBox Text="{Binding RequestBody}" Grid.Row="1" AcceptsReturn="True" TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" Margin="0,0.333,-52,-0.333" />
</Grid>
<!-- 响应内容展示框 -->
<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" />
<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>
<!-- 加载进度条 -->
<Grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2">
<ProgressBar IsIndeterminate="{Binding IsLoading}" Height="10"
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVisibilityConverter}}"></ProgressBar>
</Grid>
</Grid>
</Window>
3. 关键要点
(1)数据绑定
-
Window.DataContext:绑定MainViewModel作为整个窗口的数据上下文 -
控件绑定:
-
URL 输入框:
Text="{Binding Url}" -
请求方法下拉框:
SelectedValue="{Binding Method}" -
请求体输入框:
Text="{Binding RequestBody}" -
响应内容展示框:
Text="{Binding ResponseContent}"(只读) -
发送按钮:
Command="{Binding SendRequestCommand}"
-
(2)转换器使用
-
BooleanToVisibilityConverter:将IsLoading(bool)转为进度条的可见性(Visibility) -
BoolToInverseBoolConverter:反转IsLoading值,实现加载时禁用发送按钮(需补充实现该转换器类)
(3)UI 布局
-
采用 Grid 布局,分行列管理控件位置,适配窗口大小
-
请求体和响应内容使用多行文本框(
AcceptsReturn="True"),支持换行和滚动条
五、补充说明(缺失的转换器实现)
代码中引用了BoolToInverseBoolConverter但未实现,需补充:
using System;
using System.Globalization;
using System.Windows.Data;
namespace HttpsDemoApp
{
public class BoolToInverseBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return !boolValue;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return !boolValue;
}
return false;
}
}
}
六、整体总结
1. 设计模式
-
MVVM 模式:分离 UI(View)、业务逻辑(ViewModel)、数据(Model),降低耦合
-
命令模式:通过
RelayCommand实现 UI 操作与业务逻辑的解耦
2. 核心特性
-
异步 HTTP 请求:避免 UI 阻塞,提升用户体验
-
数据双向绑定:属性变更自动同步到 UI,UI 输入自动同步到 ViewModel
-
异常处理:区分不同类型异常,友好展示错误信息
-
状态管理:加载状态控制进度条和按钮可用状态
3. 改进建议
-
增加请求头自定义功能(当前 Model 支持但 UI 未实现)
-
优化响应内容格式化(如 JSON 格式化展示)
-
添加请求超时设置(
HttpClient.Timeout) -
增加请求参数验证(如 URL 格式校验)
-
完善日志记录,便于调试