C# WPF Http Tool
By
jerryxjr1220
at 2023-10-10 • 0人收藏 • 504人看过
练手项目,涉及MVVM数据绑定,RelayCommand命令绑定,跨线程Messenger传参等,Html解析用了HtmlAgility库,递归读取DocumentNode后画到TreeView里。
using System; using System.Net; using CommunityToolkit.Mvvm.ComponentModel; namespace WPFHttpTool.Models; public partial class HttpClientModel : ObservableObject { [ObservableProperty] private WebClient _client; [Obsolete("Obsolete")] public HttpClientModel() { Client = new WebClient(); } }
using System; using System.Collections.Generic; using System.ComponentModel; using System.Net.Http.Json; using System.Text; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using WPFHttpTool.Models; using WPFHttpTool.Views; namespace WPFHttpTool.ViewModels; public partial class MainViewModel : ObservableObject { [ObservableProperty] private HttpClientModel _clientModel; [ObservableProperty] private string? _method; [ObservableProperty] private BindingList<Tuple<string, string>> _requestHeaders; [ObservableProperty] private BindingList<Tuple<string, string>> _requestParameters; [ObservableProperty] private ResultPage _resultPage = new(); [ObservableProperty] private string? _url; [Obsolete("Obsolete")] public MainViewModel() { ClientModel = new HttpClientModel(); Url = string.Empty; Method = "GET"; RequestHeaders = new BindingList<Tuple<string, string>>(); RequestParameters = new BindingList<Tuple<string, string>>(); RequestHeaders.Add(new Tuple<string, string>("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/")); #region 注册并接受消息 WeakReferenceMessenger.Default.Register<AddRequestPage.CustomizedMessage>(this, (o, m) => { RequestHeaders = m.viewmodel.RequestHeaders; RequestParameters = m.viewmodel.RequestParameters; }); #endregion } [RelayCommand] public async Task RecievedAsString() { ClientModel.Client.Headers.Clear(); switch (Method) { case "GET": foreach (var header in RequestHeaders) ClientModel.Client.Headers.Add(header.Item1, header.Item2); string? parStr = null; var sb = new StringBuilder(); foreach (var par in RequestParameters) sb.Append(par.Item1 + "=" + par.Item2 + "&"); if (sb.Length > 1) parStr = sb.ToString()[..^1]; var resp = await ClientModel.Client.DownloadStringTaskAsync(Url + "?" + parStr); ResultPage = new ResultPage(); ResultPage.RenderPage(ClientModel, resp, null); ResultPage.Show(); break; case "POST": foreach (var header in RequestHeaders) ClientModel.Client.Headers.Add(header.Item1, header.Item2); var pars = new List<string>(); foreach (var par in RequestParameters) pars.Add(par.Item2); var jObject = JsonContent.Create(pars); var resp2 = await ClientModel.Client.UploadStringTaskAsync(Url!, jObject?.ToString() ?? string.Empty); ResultPage = new ResultPage(); ResultPage.RenderPage(ClientModel, resp2, null); ResultPage.Show(); break; } } [RelayCommand] public async Task RecievedAsBytes() { ClientModel.Client.Headers.Clear(); switch (Method) { case "GET": foreach (var header in RequestHeaders) ClientModel.Client.Headers.Add(header.Item1, header.Item2); string? parStr = null; var sb = new StringBuilder(); foreach (var par in RequestParameters) sb.Append(par.Item1 + "=" + par.Item2 + "&"); if (sb.Length > 1) parStr = sb.ToString()[..^1]; var resp = await ClientModel.Client.DownloadDataTaskAsync(new Uri(Url + "?" + parStr)); ResultPage = new ResultPage(); ResultPage.RenderPage(ClientModel, null, resp); ResultPage.Show(); break; case "POST": foreach (var header in RequestHeaders) ClientModel.Client.Headers.Add(header.Item1, header.Item2); var pars = new List<string>(); foreach (var par in RequestParameters) pars.Add(par.Item2); var jObject = JsonContent.Create(pars); var resp2 = await ClientModel.Client.UploadStringTaskAsync(Url!, jObject?.ToString() ?? string.Empty); ResultPage = new ResultPage(); ResultPage.RenderPage(ClientModel, resp2, null); ResultPage.Show(); break; } } }
<hc:GlowWindow x:Class="WPFHttpTool.Views.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:vm="clr-namespace:WPFHttpTool.ViewModels" xmlns:hc="https://handyorg.github.io/handycontrol" d:DataContext="{d:DesignInstance Type=vm:MainViewModel}" mc:Ignorable="d" Title="
4 个回复 | 最后更新于 2023-10-11
jerryxjr1220
2023-10-10
#1
2023-10-10
#2
<Window x:Class="WPFHttpTool.Views.AddRequestPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:hc="https://handyorg.github.io/handycontrol" xmlns:vm="clr-namespace:WPFHttpTool.ViewModels" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=vm:MainViewModel}" Title="Add..." Height="280" Width="400" ResizeMode="NoResize"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBox x:Name="tbKey" Width="300" Margin="10" hc:InfoElement.Title="Key" hc:InfoElement.TitlePlacement="Left" /> <TextBox x:Name="tbValue" Width="300" Height="150" MaxLines="6" VerticalContentAlignment="Top" hc:InfoElement.Title="Value" hc:InfoElement.TitlePlacement="Left" /> <Button Content="Submit" Style="{StaticResource ButtonPrimary}" Margin="5" Click="ButtonSubmit_OnClick" /> </StackPanel> </Window>
using System; using System.Windows; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; using WPFHttpTool.ViewModels; namespace WPFHttpTool.Views; public partial class AddRequestPage : Window { private readonly string _buttonName; private readonly MainViewModel _mainViewModel; public AddRequestPage(MainViewModel _viewModel, string _name) { InitializeComponent(); _mainViewModel = _viewModel; _buttonName = _name; } private void ButtonSubmit_OnClick(object sender, RoutedEventArgs e) { if (_buttonName == "ButtonAddHeader") _mainViewModel.RequestHeaders.Add(new Tuple<string, string>(tbKey.Text, tbValue.Text)); else _mainViewModel.RequestParameters.Add(new Tuple<string, string>(tbKey.Text, tbValue.Text)); var cm = new CustomizedMessage(_mainViewModel); WeakReferenceMessenger.Default.Send(cm); Close(); } //定义消息 public class CustomizedMessage : ValueChangedMessage<MainViewModel> // PropertyChangedMessage // RequestMessage ( Async + ..) { public MainViewModel viewmodel; public CustomizedMessage(MainViewModel value) : base(value) { viewmodel = value; } } }
2023-10-10
#3
<Window x:Class="WPFHttpTool.Views.ResultPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WPFHttpTool.Views" xmlns:hc="https://handyorg.github.io/handycontrol" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=local:ResultPage}" Title="Received Response" Height="850" Width="800" ResizeMode="NoResize"> <StackPanel> <hc:Shield Status="Headers" Subject="Response" Color="Green" Margin="10,5" /> <DataGrid ItemsSource="{Binding RespHeaders}" AutoGenerateColumns="False" IsReadOnly="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Height="350" Width="780"> <DataGrid.Columns> <DataGridTextColumn Header="Key" Binding="{Binding Item1 }" /> <DataGridTextColumn Header="Value" Binding="{Binding Item2 }" /> </DataGrid.Columns> </DataGrid> <WrapPanel> <hc:Shield Status="Html" Subject="Response" Color="Green" Margin="10,5" /> <Button Content="Parse" Style="{StaticResource ButtonSuccess.Small}" Command="{Binding ParseHtmlCommand}" /> </WrapPanel> <TextBox MaxLines="21" VerticalScrollBarVisibility="Auto" IsReadOnly="True" Text="{Binding ReceivedString}" TextWrapping="Wrap" /> <hc:Shield Status="Bytes Length" Subject="Response" Color="Green" Margin="10,5" /> <TextBox IsReadOnly="True" Text="{Binding ReceivedBytesLength}" /> </StackPanel> </Window>
using System; using System.Collections.ObjectModel; using System.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using HtmlAgilityPack; using WPFHttpTool.Models; namespace WPFHttpTool.Views; [ObservableObject] public partial class ResultPage : Window { [ObservableProperty] private HttpClientModel? _clientModel; [ObservableProperty] private byte[] _receivedBytes = Array.Empty<byte>(); [ObservableProperty] private int _receivedBytesLength; [ObservableProperty] private string? _receivedString; [ObservableProperty] private ObservableCollection<Tuple<string, string>> _respHeaders; public ResultPage() { InitializeComponent(); RespHeaders = new ObservableCollection<Tuple<string, string>>(); DataContext = this; } public void RenderPage(HttpClientModel _httpClientModel, string? receivedString, byte[]? receivedBytes) { ClientModel = _httpClientModel; ReceivedString = receivedString; if (receivedBytes is not null) ReceivedBytes = receivedBytes; ReceivedBytesLength = ReceivedBytes.Length; var keys = ClientModel.Client.ResponseHeaders?.AllKeys; foreach (var key in keys) { var value = ClientModel.Client.ResponseHeaders?.GetValues(key)?[0]; if (value is not null) RespHeaders.Add(new Tuple<string, string>(key, value)); } } [RelayCommand] public void ParseHtml() { var htmlDocument = new HtmlDocument(); htmlDocument.LoadHtml(ReceivedString); var treeViewPage = new TreeViewPage(htmlDocument); treeViewPage.Show(); } }
<Window x:Class="WPFHttpTool.Views.TreeViewPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" Title="TreeViewPage" Height="800" Width="800"> <Grid> <TreeView x:Name="_treeView" /> </Grid> </Window>
using System.Windows; using System.Windows.Controls; using CommunityToolkit.Mvvm.ComponentModel; using HtmlAgilityPack; namespace WPFHttpTool.Views; [ObservableObject] public partial class TreeViewPage : Window { [ObservableProperty] private HtmlDocument _htmlDocument; public TreeViewPage(HtmlDocument htmlDocument) { InitializeComponent(); _htmlDocument = htmlDocument; MapHtmlToTreeView(_htmlDocument.DocumentNode); } public void MapHtmlToTreeView(HtmlNode htmlNode, TreeViewItem parentItem = null) { foreach (var childNode in htmlNode.ChildNodes) if (childNode.NodeType == HtmlNodeType.Element) { var treeViewItem = new TreeViewItem(); treeViewItem.Header = childNode.Name; foreach (var attr in childNode.Attributes) treeViewItem.Header += $" {attr.Name} : {attr.Value} ;"; if (parentItem != null) parentItem.Items.Add(treeViewItem); else _treeView.Items.Add(treeViewItem); MapHtmlToTreeView(childNode, treeViewItem); } else if (childNode.NodeType == HtmlNodeType.Text) { var text = childNode.InnerHtml.Trim(); if (!string.IsNullOrEmpty(text)) { var textBlock = new TextBlock(); textBlock.Text = text; if (parentItem != null) parentItem.Items.Add(textBlock); else _treeView.Items.Add(textBlock); } } } }
2023-10-11
#4
增加了Json字符串解析功能,可以抓取Json API的返回值,直接解析,自动判断类型,并绘制TreeView展示。
用了NewtonSoft.Json库进行Json字符串的解析。
using System; using System.Windows; using System.Windows.Controls; using CommunityToolkit.Mvvm.ComponentModel; using HtmlAgilityPack; using Newtonsoft.Json.Linq; namespace WPFHttpTool.Views; [ObservableObject] public partial class TreeViewPage : Window { [ObservableProperty] private HtmlDocument? _htmlDocument; public TreeViewPage(HtmlDocument htmlDocument) { InitializeComponent(); _htmlDocument = htmlDocument; MapHtmlToTreeView(_htmlDocument.DocumentNode); } public TreeViewPage(JObject jObject) { InitializeComponent(); MapJsonToTreeView(jObject); } public TreeViewPage(JArray jArray) { InitializeComponent(); MapJsonToTreeView(jArray); } public void MapHtmlToTreeView(HtmlNode htmlNode, TreeViewItem parentItem = null) { foreach (var childNode in htmlNode.ChildNodes) { if (childNode.NodeType == HtmlNodeType.Element) { var treeViewItem = new TreeViewItem(); treeViewItem.Header = childNode.Name; foreach (var attr in childNode.Attributes) treeViewItem.Header += $" {attr.Name} : {attr.Value} ;"; if (parentItem != null) parentItem.Items.Add(treeViewItem); else _treeView.Items.Add(treeViewItem); MapHtmlToTreeView(childNode, treeViewItem); } else if (childNode.NodeType == HtmlNodeType.Text) { var text = childNode.InnerHtml.Trim(); if (!string.IsNullOrEmpty(text)) { var textBlock = new TextBlock(); textBlock.Text = text; if (parentItem != null) parentItem.Items.Add(textBlock); else _treeView.Items.Add(textBlock); } } } } public void MapJsonToTreeView(JObject jObject, TreeViewItem parentItem = null) { foreach (var (key, value) in jObject) { var treeViewItem = new TreeViewItem(); treeViewItem.Header = key; try { var children = value!.ToObject<JObject>(); MapJsonToTreeView(children!, treeViewItem); } catch (Exception ex) { try { var children = value!.ToObject<JArray>(); MapJsonToTreeView(children!, treeViewItem); } catch (Exception ex2) { var child = value!.ToObject<string>(); treeViewItem.Header += $" : {child}, "; } } if (parentItem != null) parentItem.Items.Add(treeViewItem); else _treeView.Items.Add(treeViewItem); } } public void MapJsonToTreeView(JArray jArray, TreeViewItem parentItem = null) { foreach (var jtoken in jArray) { try { var children = jtoken.ToObject<JObject>(); MapJsonToTreeView(children!, parentItem); } catch (Exception ex) { try { var children = jtoken.ToObject<JArray>(); MapJsonToTreeView(children!, parentItem); } catch (Exception ex2) { var child = jtoken.ToObject<string>(); if (parentItem != null) parentItem.Header += $" {child},"; else _treeView.Items.Add(new TextBlock { Text = child }); } } } } }
登录后方可回帖