Compare commits
10 Commits
e44b2895c9
...
0c2334cefb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c2334cefb | ||
|
|
dd7f1c1ea0 | ||
|
|
4bb242a4cc | ||
|
|
b577067379 | ||
|
|
bd4d4856ea | ||
|
|
c3a12ba5b7 | ||
|
|
478bf13a4a | ||
|
|
d8849f48da | ||
|
|
c466c553dc | ||
|
|
48afa6b433 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -409,3 +409,4 @@ FodyWeavers.xsd
|
|||||||
/DigitalData.Core.ConsoleApp/Program.cs
|
/DigitalData.Core.ConsoleApp/Program.cs
|
||||||
/DigitalData.Core.ConsoleApp/FooHttpOptions.cs
|
/DigitalData.Core.ConsoleApp/FooHttpOptions.cs
|
||||||
/DigitalData.Core.Tests/obj/
|
/DigitalData.Core.Tests/obj/
|
||||||
|
/DigitalData.Core.Terminal
|
||||||
|
|||||||
@@ -4,12 +4,15 @@ namespace DigitalData.Core.Abstractions.Client
|
|||||||
{
|
{
|
||||||
public interface IBaseHttpClientService
|
public interface IBaseHttpClientService
|
||||||
{
|
{
|
||||||
public string Uri { get; init; }
|
string Uri { get; init; }
|
||||||
|
|
||||||
public CookieCollection GetCookies(string route = "");
|
CookieCollection GetCookies(string path = "");
|
||||||
|
|
||||||
Task<HttpResponseMessage> FetchAsync(
|
Task<HttpResponseMessage> FetchAsync(
|
||||||
string route = "",
|
string? scheme = null,
|
||||||
|
int? port = null,
|
||||||
|
string path = "",
|
||||||
|
Dictionary<string, object?>? queryParams = null,
|
||||||
HttpMethod? method = null,
|
HttpMethod? method = null,
|
||||||
HttpContent? body = null,
|
HttpContent? body = null,
|
||||||
Dictionary<string, string>? form = null,
|
Dictionary<string, string>? form = null,
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
{
|
{
|
||||||
public interface IHttpClientOptions
|
public interface IHttpClientOptions
|
||||||
{
|
{
|
||||||
public string Uri { get; init; }
|
public string Uri { get; set; }
|
||||||
|
|
||||||
|
public string Path { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,9 +16,10 @@
|
|||||||
<PackageProjectUrl></PackageProjectUrl>
|
<PackageProjectUrl></PackageProjectUrl>
|
||||||
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
||||||
<PackAsTool>False</PackAsTool>
|
<PackAsTool>False</PackAsTool>
|
||||||
<NeutralLanguage>aa-DJ</NeutralLanguage>
|
|
||||||
<PackageIcon>core_icon.png</PackageIcon>
|
<PackageIcon>core_icon.png</PackageIcon>
|
||||||
<Version>2.0.0.0</Version>
|
<Version>2.1.0.0</Version>
|
||||||
|
<AssemblyVersion>2.1.0.0</AssemblyVersion>
|
||||||
|
<FileVersion>2.1.0.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Web;
|
||||||
|
|
||||||
namespace DigitalData.Core.Client
|
namespace DigitalData.Core.Client
|
||||||
{
|
{
|
||||||
@@ -13,17 +14,23 @@ namespace DigitalData.Core.Client
|
|||||||
[StringSyntax("Uri")]
|
[StringSyntax("Uri")]
|
||||||
public string Uri { get; init; }
|
public string Uri { get; init; }
|
||||||
|
|
||||||
|
public string Path { get; init; } = string.Empty;
|
||||||
|
|
||||||
public BaseHttpClientService(HttpClient client, CookieContainer cookieContainer, IOptions<HttpClientOptions> clientOptions)
|
public BaseHttpClientService(HttpClient client, CookieContainer cookieContainer, IOptions<HttpClientOptions> clientOptions)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_cookies = cookieContainer;
|
_cookies = cookieContainer;
|
||||||
Uri = clientOptions.Value.Uri;
|
Uri = clientOptions.Value.Uri.Trim(URI_TRIM_CHARS);
|
||||||
|
Path = clientOptions.Value.Path.Trim(URI_TRIM_CHARS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CookieCollection GetCookies(string route = "") => _cookies.GetCookies(uri: new Uri(Uri + route));
|
public CookieCollection GetCookies(string path = "") => _cookies.GetCookies(uri: new Uri(UriCombine(Uri, path, path.Trim(URI_TRIM_CHARS))));
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> FetchAsync(
|
public async Task<HttpResponseMessage> FetchAsync(
|
||||||
string route = "",
|
string? scheme = null,
|
||||||
|
int? port = null,
|
||||||
|
string path = "",
|
||||||
|
Dictionary<string, object?>? queryParams = null,
|
||||||
HttpMethod? method = null,
|
HttpMethod? method = null,
|
||||||
HttpContent? body = null,
|
HttpContent? body = null,
|
||||||
Dictionary<string, string>? form = null,
|
Dictionary<string, string>? form = null,
|
||||||
@@ -36,10 +43,37 @@ namespace DigitalData.Core.Client
|
|||||||
method ??= HttpMethod.Get;
|
method ??= HttpMethod.Get;
|
||||||
|
|
||||||
// create URL
|
// create URL
|
||||||
var requestUriStr = Uri + route;
|
var uriBuilder = new UriBuilder(Uri);
|
||||||
var requestUri = new Uri(requestUriStr);
|
if (scheme is not null)
|
||||||
|
uriBuilder.Scheme = scheme;
|
||||||
|
if (port is int portInt)
|
||||||
|
uriBuilder.Port = portInt;
|
||||||
|
uriBuilder.Path = UriCombine(Path, path?.Trim(URI_TRIM_CHARS) ?? string.Empty);
|
||||||
|
|
||||||
var requestMessage = new HttpRequestMessage(method, requestUriStr);
|
// Add query parameters if provided
|
||||||
|
if (queryParams?.Any() ?? false)
|
||||||
|
{
|
||||||
|
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
|
||||||
|
|
||||||
|
var flagParams = queryParams.Where(param => param.Value is null).Select(param => HttpUtility.UrlEncode(param.Key));
|
||||||
|
|
||||||
|
var valueParams = queryParams.Where(param => param.Value is not null);
|
||||||
|
|
||||||
|
foreach (var param in valueParams)
|
||||||
|
query[param.Key] = param.Value switch
|
||||||
|
{
|
||||||
|
bool b => b.ToString().ToLower(),
|
||||||
|
_ => HttpUtility.UrlEncode(param.Value.ToString())
|
||||||
|
};
|
||||||
|
|
||||||
|
var flagQuery = string.Join(QUERY_SEPARATOR, flagParams);
|
||||||
|
|
||||||
|
uriBuilder.Query = string.Join(QUERY_SEPARATOR, query.ToString(), flagQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestUri = uriBuilder.Uri;
|
||||||
|
Console.WriteLine(requestUri);
|
||||||
|
var requestMessage = new HttpRequestMessage(method, requestUri);
|
||||||
|
|
||||||
// Add headers if provided
|
// Add headers if provided
|
||||||
headers?.ForEach(header => requestMessage.Headers.Add(header.Key, header.Value));
|
headers?.ForEach(header => requestMessage.Headers.Add(header.Key, header.Value));
|
||||||
@@ -72,5 +106,11 @@ namespace DigitalData.Core.Client
|
|||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static readonly char[] URI_TRIM_CHARS = { '\\', '/', ' ' };
|
||||||
|
|
||||||
|
internal static string UriCombine(params string[] paths) => System.IO.Path.Combine(paths).Replace("\\", "/");
|
||||||
|
|
||||||
|
internal static readonly char QUERY_SEPARATOR = '&';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,12 +7,16 @@ namespace DigitalData.Core.Client
|
|||||||
{
|
{
|
||||||
public static class DIExtensions
|
public static class DIExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddHttpClientService(this IServiceCollection services, string uri)
|
public static IServiceCollection AddHttpClientService(this IServiceCollection services, string uri, string path = "")
|
||||||
{
|
{
|
||||||
services.TryAddSingleton<HttpClient>();
|
services.TryAddSingleton<HttpClient>();
|
||||||
services.TryAddSingleton<CookieContainer>();
|
services.TryAddSingleton<CookieContainer>();
|
||||||
services.AddSingleton<IBaseHttpClientService, BaseHttpClientService>();
|
services.AddSingleton<IBaseHttpClientService, BaseHttpClientService>();
|
||||||
services.Configure<HttpClientOptions>(opt => opt.Uri = uri);
|
services.Configure<HttpClientOptions>(opt =>
|
||||||
|
{
|
||||||
|
opt.Uri = uri;
|
||||||
|
opt.Path = path;
|
||||||
|
});
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@@ -22,11 +26,11 @@ namespace DigitalData.Core.Client
|
|||||||
{
|
{
|
||||||
services.TryAddSingleton<HttpClient>();
|
services.TryAddSingleton<HttpClient>();
|
||||||
services.TryAddSingleton<CookieContainer>();
|
services.TryAddSingleton<CookieContainer>();
|
||||||
services.AddSingleton<IHttpClientService<TClientOptions>, HttpClientService<TClientOptions>>();
|
services.TryAddSingleton<IHttpClientService<TClientOptions>, HttpClientService<TClientOptions>>();
|
||||||
services.Configure(clientOptions ?? (_ => { }));
|
services.Configure(clientOptions ?? (_ => { }));
|
||||||
|
|
||||||
if (setAsDefaultBase)
|
if (setAsDefaultBase)
|
||||||
services.AddSingleton<IBaseHttpClientService, HttpClientService<TClientOptions>>();
|
services.TryAddSingleton<IBaseHttpClientService, HttpClientService<TClientOptions>>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
|
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Description>This package provides HTTP client extension methods for the DigitalData.Core library, offering simplified and asynchronous methods for fetching and handling HTTP responses. It includes utility methods for sending GET requests, reading response content as text or JSON, and deserializing JSON into dynamic or strongly-typed objects using Newtonsoft.Json. These extensions facilitate efficient and easy-to-read HTTP interactions in client applications.</Description>
|
<Description>This package provides HTTP client extension methods for the DigitalData.Core library, offering simplified and asynchronous methods for fetching and handling HTTP responses. It includes utility methods for sending GET requests, reading response content as text or JSON, and deserializing JSON into dynamic or strongly-typed objects using Newtonsoft.Json. These extensions facilitate efficient and easy-to-read HTTP interactions in client applications.</Description>
|
||||||
<PackageId>DigitalData.Core.Client</PackageId>
|
<PackageId>DigitalData.Core.Client</PackageId>
|
||||||
<Version>1.0.1.1</Version>
|
<Version>1.1.0</Version>
|
||||||
<Authors>Digital Data GmbH</Authors>
|
<Authors>Digital Data GmbH</Authors>
|
||||||
<Company>Digital Data GmbH</Company>
|
<Company>Digital Data GmbH</Company>
|
||||||
<Product>Digital Data GmbH</Product>
|
<Product>Digital Data GmbH</Product>
|
||||||
@@ -15,6 +15,8 @@
|
|||||||
<PackageIcon>core_icon.png</PackageIcon>
|
<PackageIcon>core_icon.png</PackageIcon>
|
||||||
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
|
||||||
<PackageTags>digital data core http client json serilization</PackageTags>
|
<PackageTags>digital data core http client json serilization</PackageTags>
|
||||||
|
<AssemblyVersion>1.1.0</AssemblyVersion>
|
||||||
|
<FileVersion>1.1.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ namespace DigitalData.Core.Client
|
|||||||
{
|
{
|
||||||
public class HttpClientOptions : IHttpClientOptions
|
public class HttpClientOptions : IHttpClientOptions
|
||||||
{
|
{
|
||||||
public string Uri { get; init; } = string.Empty;
|
public string Uri { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Path { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ namespace DigitalData.Core.Client
|
|||||||
|
|
||||||
public static T Provide<T>() where T : notnull => _lazyProvider.Value.GetRequiredService<T>();
|
public static T Provide<T>() where T : notnull => _lazyProvider.Value.GetRequiredService<T>();
|
||||||
|
|
||||||
public static IHttpClientService<TOptions> ProvideHttpClientService<TOptions>() where TOptions : notnull
|
public static IHttpClientService<TOptions> ProvideHttpClientService<TOptions>() where TOptions : IHttpClientOptions
|
||||||
=> _lazyProvider.Value.GetRequiredService<IHttpClientService<TOptions>>();
|
=> _lazyProvider.Value.GetRequiredService<IHttpClientService<TOptions>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,22 +14,46 @@ namespace DigitalData.Core.Tests.Client
|
|||||||
public void SetUp()
|
public void SetUp()
|
||||||
{
|
{
|
||||||
_serviceProvider = new ServiceCollection()
|
_serviceProvider = new ServiceCollection()
|
||||||
.AddHttpClientService("https://jsonplaceholder.typicode.com/todos")
|
.AddHttpClientService("https://jsonplaceholder.typicode.com", "todos")
|
||||||
.BuildServiceProvider();
|
.BuildServiceProvider();
|
||||||
|
|
||||||
_service = _serviceProvider.GetRequiredService<IBaseHttpClientService>();
|
_service = _serviceProvider.GetRequiredService<IBaseHttpClientService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task FetchJsonAsync_ShouldReturnJsonResponse()
|
public async Task FetchJsonAsync_ShouldReturnJsonResponse_WithCorrectWithPath()
|
||||||
{
|
{
|
||||||
// Act
|
// Act
|
||||||
var expectedUserId = (int) await _service.FetchAsync("/1", sendWithCookie: false, saveCookie: false)
|
var expectedUserId = (int) await _service.FetchAsync(path: "/1", sendWithCookie: false, saveCookie: false)
|
||||||
.ThenAsync(res => res.Json())
|
.ThenAsync(res => res.Json())
|
||||||
.ThenAsync(todo => todo.userId);
|
.ThenAsync(todo => todo.userId);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.That(expectedUserId, Is.EqualTo(1), "The userId of the fetched JSON object should be 1.");
|
Assert.That(expectedUserId, Is.EqualTo(1), "The userId of the fetched JSON object should be 1.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task FetchJsonAsync_ShouldReturnJsonResponse_WithQueryParams()
|
||||||
|
{
|
||||||
|
var queryParams = new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
{ "id", "1" }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var dyn_id = await _service.FetchAsync(queryParams: queryParams, sendWithCookie: false, saveCookie: false)
|
||||||
|
.ThenAsync(res => res.JsonList())
|
||||||
|
.ThenAsync(todo => todo.FirstOrDefault()?.userId);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.That((int)dyn_id, Is.EqualTo(1), "The userId of the fetched JSON object should be 1.");
|
||||||
|
}
|
||||||
|
catch (InvalidCastException)
|
||||||
|
{
|
||||||
|
// Handle the case where the cast is not possible
|
||||||
|
Assert.Fail("The id could not be cast to an integer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Core.Legacy.Cli
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Security", "DigitalData.Core.Security\DigitalData.Core.Security.csproj", "{47D80C65-74A2-4EB8-96A5-D571A9108FB3}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Security", "DigitalData.Core.Security\DigitalData.Core.Security.csproj", "{47D80C65-74A2-4EB8-96A5-D571A9108FB3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Core.Security.Extensions", "DigitalData.Core.Security.Extensions\DigitalData.Core.Security.Extensions.csproj", "{D740182D-82DA-480A-9F87-BFB4A8620A00}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Security.Extensions", "DigitalData.Core.Security.Extensions\DigitalData.Core.Security.Extensions.csproj", "{D740182D-82DA-480A-9F87-BFB4A8620A00}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Core.Terminal", "DigitalData.Core.Terminal\DigitalData.Core.Terminal.csproj", "{0FA93730-8084-4907-B172-87D610323796}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -74,6 +76,10 @@ Global
|
|||||||
{D740182D-82DA-480A-9F87-BFB4A8620A00}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{D740182D-82DA-480A-9F87-BFB4A8620A00}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{D740182D-82DA-480A-9F87-BFB4A8620A00}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{D740182D-82DA-480A-9F87-BFB4A8620A00}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{D740182D-82DA-480A-9F87-BFB4A8620A00}.Release|Any CPU.Build.0 = Release|Any CPU
|
{D740182D-82DA-480A-9F87-BFB4A8620A00}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0FA93730-8084-4907-B172-87D610323796}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0FA93730-8084-4907-B172-87D610323796}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0FA93730-8084-4907-B172-87D610323796}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0FA93730-8084-4907-B172-87D610323796}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Reference in New Issue
Block a user