WPF HTTPS 通信示例代码分析笔记

一、项目整体结构

该项目是一个基于 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 格式校验)

  • 完善日志记录,便于调试

相关推荐
吃喝不愁霸王餐APP开发者2 小时前
Java应用对接美团开放平台API时的HTTPS双向认证与证书管理实践
java·开发语言·https
polarislove02142 小时前
5.7W25Q64 实验(上)-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件
游戏开发爱好者82 小时前
uni-app 项目在 iOS 上架过程中常见的问题与应对方式
android·ios·小程序·https·uni-app·iphone·webview
im_AMBER2 小时前
Leetcode 84 水果成篮 | 删除子数组的最大得分
数据结构·c++·笔记·学习·算法·leetcode·哈希算法
hssfscv2 小时前
Javaweb学习笔记——Maven
笔记·学习·maven
廋到被风吹走2 小时前
【Spring】ThreadLocal详解 线程隔离的魔法与陷阱
java·spring·wpf
d111111111d3 小时前
STM32-HAL库学习,初识HAL库
笔记·stm32·单片机·嵌入式硬件·学习
Roxanne0073 小时前
吴教授《AI for everyone》笔记梳理(DAY1)
人工智能·笔记
初来乍到到3 小时前
elementUI表格tableixed=“right“ 导致固定列错位的解决方法
笔记