A
This commit is contained in:
1 parent
ab3721912d
commit
a3827a17f2
17 files changed
+319
-31
No files matched your search
@@ -7,5 +7,18 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Wpf\Wpf.csproj" />
|
||||
<ProjectReference Include="..\Config\Config.csproj" />
|
||||
<ProjectReference Include="..\NetClient\NetClient.csproj" />
|
||||
<ProjectReference Include="..\NetServer\NetServer.csproj" />
|
||||
<ProjectReference Include="..\Log\Log.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,12 +1,39 @@
|
||||
<Window x:Class="Client.MainWindow"
|
||||
<Window x:Class="Client.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:Client"
|
||||
xmlns:b="clr-namespace:Sharp.Wpf.Behaviors;assembly=Sharp.Wpf"
|
||||
xmlns:w="clr-namespace:Sharp.Wpf.Windowing;assembly=Sharp.Wpf"
|
||||
xmlns:conv="clr-namespace:Sharp.Wpf.Converters;assembly=Sharp.Wpf"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="450" Width="800">
|
||||
<Grid>
|
||||
|
||||
Title="MainWindow" Height="450" Width="800"
|
||||
w:WindowPlacement.RememberPlacement="True"
|
||||
w:WindowPlacement.PlacementId="MainWindow">
|
||||
<Window.Resources>
|
||||
<conv:BoolToVisibilityConverter x:Key="Bool2Vis" />
|
||||
</Window.Resources>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="Sharp.Wpf 集成示例" FontSize="18" FontWeight="Bold"/>
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="1" Margin="0,8,0,0">
|
||||
<Button Content="点击弹窗"
|
||||
Margin="0,0,8,0"
|
||||
b:EventToCommand.EventName="Click"
|
||||
b:EventToCommand.Command="{Binding ShowDialogCommand}" />
|
||||
<Button Content="触发异常"
|
||||
b:EventToCommand.EventName="Click"
|
||||
b:EventToCommand.Command="{Binding ThrowCommand}" />
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Row="2" Text="正在处理..." Foreground="DarkOrange"
|
||||
Visibility="{Binding IsBusy, Converter={StaticResource Bool2Vis}}"/>
|
||||
<TextBlock Grid.Row="3" Text="{Binding Greeting}" VerticalAlignment="Top"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using Client.ViewModels;
|
||||
|
||||
namespace Client
|
||||
{
|
||||
@@ -19,6 +11,8 @@ namespace Client
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new MainViewModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
@@ -6,4 +6,18 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog" Version="4.0.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Process" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Common.Results;
|
||||
|
||||
namespace NetClient.Abstractions;
|
||||
|
||||
@@ -18,5 +19,14 @@ public interface IHttpClient
|
||||
Task DeleteAsync(string path, object? query = null, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<TResponse?> PostMultipartAsync<TResponse>(string path, System.Action<System.Net.Http.MultipartFormDataContent> buildContent, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
// Result-based convenience methods (non-throwing)
|
||||
Task<Result<T>> GetResultAsync<T>(string path, object? query = null, CancellationToken cancellationToken = default);
|
||||
Task<Result<string>> GetStringResultAsync(string path, object? query = null, CancellationToken cancellationToken = default);
|
||||
Task<Result<byte[]>> GetBytesResultAsync(string path, object? query = null, CancellationToken cancellationToken = default);
|
||||
Task<Result<TResponse>> PostResultAsync<TRequest, TResponse>(string path, TRequest body, CancellationToken cancellationToken = default);
|
||||
Task<Result<TResponse>> PutResultAsync<TRequest, TResponse>(string path, TRequest body, CancellationToken cancellationToken = default);
|
||||
Task<Result<TResponse>> PatchResultAsync<TRequest, TResponse>(string path, TRequest body, CancellationToken cancellationToken = default);
|
||||
Task<Result> DeleteResultAsync(string path, object? query = null, CancellationToken cancellationToken = default);
|
||||
Task<Result<TResponse>> PostMultipartResultAsync<TResponse>(string path, System.Action<System.Net.Http.MultipartFormDataContent> buildContent, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public static class ServiceCollectionExtensions
|
||||
{
|
||||
services.TryAddTransient<LoggingHandler>();
|
||||
services.TryAddTransient<BearerTokenAuthenticationHandler>();
|
||||
services.TryAddSingleton<IHttpContentSerializer>(sp => new SystemTextJsonContentSerializer(new System.Text.Json.JsonSerializerOptions(System.Text.Json.JsonSerializerDefaults.Web)));
|
||||
services.TryAddSingleton<IHttpContentSerializer>(sp => new SystemTextJsonContentSerializer(Common.Utils.JsonHelper.CamelCaseOptions));
|
||||
services.TryAddSingleton<INetClientFactory, NetClientFactory>();
|
||||
return services;
|
||||
}
|
||||
@@ -108,4 +108,3 @@ public static class ServiceCollectionExtensions
|
||||
return policyBuilder.WaitAndRetryAsync(delay);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Common.Results;
|
||||
using NetClient.Abstractions;
|
||||
using NetClient.Exceptions;
|
||||
using NetClient.Internals.Url;
|
||||
@@ -85,6 +86,150 @@ public class DefaultNetClient : IHttpClient
|
||||
return await _serializer.DeserializeAsync<TResponse>(stream, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<Result<T>> GetResultAsync<T>(string path, object? query = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = await GetAsync<T>(path, query, cancellationToken).ConfigureAwait(false);
|
||||
return Result.Success(value!);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
return ToFailureResult<T>(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<T>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<string>> GetStringResultAsync(string path, object? query = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = await GetStringAsync(path, query, cancellationToken).ConfigureAwait(false);
|
||||
return Result.Success(value);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
return ToFailureResult<string>(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<string>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<byte[]>> GetBytesResultAsync(string path, object? query = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = await GetBytesAsync(path, query, cancellationToken).ConfigureAwait(false);
|
||||
return Result.Success(value);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
return ToFailureResult<byte[]>(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<byte[]>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<TResponse>> PostResultAsync<TRequest, TResponse>(string path, TRequest body, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = await PostAsync<TRequest, TResponse>(path, body, cancellationToken).ConfigureAwait(false);
|
||||
return Result.Success(value!);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
return ToFailureResult<TResponse>(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<TResponse>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<TResponse>> PutResultAsync<TRequest, TResponse>(string path, TRequest body, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = await PutAsync<TRequest, TResponse>(path, body, cancellationToken).ConfigureAwait(false);
|
||||
return Result.Success(value!);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
return ToFailureResult<TResponse>(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<TResponse>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<TResponse>> PatchResultAsync<TRequest, TResponse>(string path, TRequest body, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = await PatchAsync<TRequest, TResponse>(path, body, cancellationToken).ConfigureAwait(false);
|
||||
return Result.Success(value!);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
return ToFailureResult<TResponse>(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<TResponse>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result> DeleteResultAsync(string path, object? query = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DeleteAsync(path, query, cancellationToken).ConfigureAwait(false);
|
||||
return Result.Success();
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
return ToFailureResult<object>(ex).Ignore();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Result<TResponse>> PostMultipartResultAsync<TResponse>(string path, Action<MultipartFormDataContent> buildContent, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = await PostMultipartAsync<TResponse>(path, buildContent, cancellationToken).ConfigureAwait(false);
|
||||
return Result.Success(value!);
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
return ToFailureResult<TResponse>(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Failure<TResponse>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Result<T> ToFailureResult<T>(ApiException ex)
|
||||
{
|
||||
var message = string.IsNullOrWhiteSpace(ex.ResponseContent)
|
||||
? $"HTTP {(int)ex.StatusCode}"
|
||||
: ex.ResponseContent!;
|
||||
return Result.Failure<T>((long)ex.StatusCode, message, ex);
|
||||
}
|
||||
|
||||
private string BuildUri(string path, object? query)
|
||||
{
|
||||
var q = QueryStringHelper.Build(query);
|
||||
@@ -123,4 +268,3 @@ public class DefaultNetClient : IHttpClient
|
||||
return await _serializer.DeserializeAsync<TResponse>(stream, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,16 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.*" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.*" />
|
||||
<PackageReference Include="Polly.Contrib.WaitAndRetry" Version="1.*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
<ProjectReference Include="..\Config\Config.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -17,10 +17,5 @@ public class NetClientOptions
|
||||
|
||||
public RetryOptions Retry { get; set; } = new();
|
||||
|
||||
public JsonSerializerOptions JsonSerializerOptions { get; set; } = new(JsonSerializerDefaults.Web)
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = false
|
||||
};
|
||||
public JsonSerializerOptions JsonSerializerOptions { get; set; } = Common.Utils.JsonHelper.CamelCaseOptions;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Windows;
|
||||
|
||||
namespace Repository
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Repository
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
@@ -1,10 +1,9 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本:4.0.30319.42000
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||
// 重新生成代码,这些更改将会丢失。
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -14,7 +13,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Sharp.Wpf")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+237cbd8788e3abea9c8e3f9fdb46798cde43452b")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+ab3721912dc64444eabd750973d21817cb24fbac")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Sharp.Wpf")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Sharp.Wpf")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -1 +1 @@
|
||||
2f73c8423764d0eb8aeceea01d1feea6277f92837fb5fffa27cece3438cfc6e5
|
||||
9b4de542688aa50112519013d6cf33dd03c8f285c691dd96ec4b3acd75e126bd
|
||||
Reference in new issue
Block a user