From f927ae9d1cb1d457659759b3333d6755a3f5bf0d Mon Sep 17 00:00:00 2001 From: NazarovMikhail Date: Wed, 21 Jan 2026 17:14:00 +0500 Subject: [PATCH 1/4] Drop RestHttpClientFromOptions. --- .../TestService.cs | 10 +- .../TestService.cs | 10 +- .../Models/RestHttpClientMock.cs | 25 +-- .../RestHttpClientFromOptionsTests.cs | 164 ------------------ .../RestHttpClientTests.cs | 27 ++- .../RestHttpClientFromOptions.cs | 71 -------- 6 files changed, 35 insertions(+), 272 deletions(-) delete mode 100644 src/Monq.Core.HttpClientExtensions.Tests/RestHttpClientFromOptionsTests.cs delete mode 100644 src/Monq.Core.HttpClientExtensions/RestHttpClientFromOptions.cs diff --git a/samples/Monq.Core.HttpClientExtensions.TestApp/TestService.cs b/samples/Monq.Core.HttpClientExtensions.TestApp/TestService.cs index a811d00..8098b4f 100644 --- a/samples/Monq.Core.HttpClientExtensions.TestApp/TestService.cs +++ b/samples/Monq.Core.HttpClientExtensions.TestApp/TestService.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; @@ -12,7 +12,7 @@ public interface ITestService Task TestApi(); } - public class TestService : RestHttpClientFromOptions, ITestService + public class TestService : RestHttpClient, ITestService { /// /// Инициализирует новый экземпляр класса . @@ -29,12 +29,10 @@ public TestService( ILoggerFactory loggerFactory, RestHttpClientOptions configuration, IHttpContextAccessor httpContextAccessor) : - base(optionsAccessor, - httpClient, + base(httpClient, loggerFactory, configuration, - httpContextAccessor, - optionsAccessor.Value.TestServiceUri) + httpContextAccessor) { } diff --git a/samples/Monq.Core.HttpClientExtensions.TestConsoleApp/TestService.cs b/samples/Monq.Core.HttpClientExtensions.TestConsoleApp/TestService.cs index 4671ad9..23b432d 100644 --- a/samples/Monq.Core.HttpClientExtensions.TestConsoleApp/TestService.cs +++ b/samples/Monq.Core.HttpClientExtensions.TestConsoleApp/TestService.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Monq.Core.HttpClientExtensions.TestApp; @@ -14,7 +14,7 @@ public interface ITestService Task TestApi(string auth); } - public class TestService : RestHttpClientFromOptions, ITestService + public class TestService : RestHttpClient, ITestService { ILogger _log; @@ -33,12 +33,10 @@ public TestService( ILoggerFactory loggerFactory, RestHttpClientOptions configuration, IHttpContextAccessor httpContextAccessor) : - base(optionsAccessor, - httpClient, + base(httpClient, loggerFactory, configuration, - httpContextAccessor, - optionsAccessor.Value.TestServiceUri) + httpContextAccessor) { _log = loggerFactory.CreateLogger(); } diff --git a/src/Monq.Core.HttpClientExtensions.Tests/Models/RestHttpClientMock.cs b/src/Monq.Core.HttpClientExtensions.Tests/Models/RestHttpClientMock.cs index 48be20e..48c9b37 100644 --- a/src/Monq.Core.HttpClientExtensions.Tests/Models/RestHttpClientMock.cs +++ b/src/Monq.Core.HttpClientExtensions.Tests/Models/RestHttpClientMock.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Net.Http; @@ -23,27 +23,4 @@ public RestHttpClientMock(HttpClient httpClient, } } - - public class RestHttpClientFromOptionsMock : RestHttpClientFromOptions - { - /// - /// Initializes a new instance of the class. - /// - /// The options. - /// The HttpClient from http client factory. - /// The logger factory. - /// The configuration. - /// The HTTP context accessor. - /// The base Uri of all requests. For example: http://rsm.api.monq.cloud - public RestHttpClientFromOptionsMock(IOptions optionsAccessor, - HttpClient httpClient, - ILoggerFactory loggerFactory, - RestHttpClientOptions configuration, - IHttpContextAccessor httpContextAccessor, - string baseUri) - : base(optionsAccessor, httpClient, loggerFactory, configuration, httpContextAccessor, baseUri) - { - - } - } } diff --git a/src/Monq.Core.HttpClientExtensions.Tests/RestHttpClientFromOptionsTests.cs b/src/Monq.Core.HttpClientExtensions.Tests/RestHttpClientFromOptionsTests.cs deleted file mode 100644 index f2062e4..0000000 --- a/src/Monq.Core.HttpClientExtensions.Tests/RestHttpClientFromOptionsTests.cs +++ /dev/null @@ -1,164 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Monq.Core.HttpClientExtensions.Tests.Models; -using Monq.Core.HttpClientExtensions.Tests.Stubs; -using Moq; -using Moq.Protected; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace Monq.Core.HttpClientExtensions.Tests -{ - public class RestHttpClientFromOptionsTests - { - readonly Mock> _optionsMoq; - - readonly IList _testCollection = new List { - new Service { Id = 1, Name = "Service1" }, - new Service { Id = 2, Name = "Service2" } - }; - - readonly ILoggerFactory _loggerFactory = new StubLoggerFactory(new List()); - readonly RestHttpClientOptions _configuration = new RestHttpClientOptions(); - - public RestHttpClientFromOptionsTests() - { - _optionsMoq = new Mock>(); - _optionsMoq - .Setup(x => x.Value) - .Returns(new ServiceOptions() - { - ServiceUri = "http://unittest" - }); - } - - [Fact(DisplayName = "Проверка установки Bearer token из HttpContextAccessor.")] - public void ShouldProperlySetBearerTokenFromRequest() - { - var httpContext = new DefaultHttpContext(); - httpContext.Request.Headers.Add("Authorization", "Bearer token355"); - - var client = new HttpClient(CreateDefaultResponseHandler(HttpStatusCode.Unauthorized)); - - var httpService = CreateBasicHttpService(client, httpContext); - - Assert.Equal("token355", client.DefaultRequestHeaders?.Authorization?.Parameter); - Assert.Equal("Bearer", client.DefaultRequestHeaders?.Authorization?.Scheme); - } - - [Fact(DisplayName = "Проверка НЕустановки Bearer token из HttpContextAccessor.")] - public void ShouldNotSetBearerTokenFromRequestIfBearerNotValid() - { - var httpContext = new DefaultHttpContext(); - httpContext.Request.Headers.Add("Authorization", "Bearetoken355"); - var client = new HttpClient(CreateDefaultResponseHandler(HttpStatusCode.Unauthorized)); - - var httpService = CreateBasicHttpService(client, httpContext); - Assert.Null(client.DefaultRequestHeaders.Authorization); - } - - [Fact(DisplayName = "Добавить слэш в BaseUri, если требуется.")] - public void ShouldAddTrailingSlashToBaseUri() - { - var httpContext = new DefaultHttpContext(); - httpContext.Request.Headers.Add("Authorization", "Bearetoken355"); - - _optionsMoq - .Setup(x => x.Value) - .Returns(new ServiceOptions() - { - ServiceUri = "http://unittest" - }); - - var client = new HttpClient(CreateDefaultResponseHandler(HttpStatusCode.Unauthorized)); - - var httpService = CreateRestHttpClientFromOptions(client, httpContext, _optionsMoq.Object); - Assert.Equal(new Uri("http://unittest/"), client.BaseAddress); - } - - [Fact(DisplayName = "Выполнить запрос по абсолютному Uri.")] - public async Task ShouldProperlyMakeRequestByAbsoluteUri() - { - var httpContext = new DefaultHttpContext(); - httpContext.Request.Headers.Add("Authorization", "Bearetoken355"); - - _optionsMoq - .Setup(x => x.Value) - .Returns(new ServiceOptions() - { - ServiceUri = "http://unittest" - }); - - var client = new HttpClient(CreateDefaultResponseHandler(HttpStatusCode.OK)); - - var httpService = CreateRestHttpClientFromOptions(client, httpContext, _optionsMoq.Object); - var result = await httpService.Get>("http://unittest/api/services"); - - Assert.Equal(result.ResultObject, _testCollection); - } - - [Fact(DisplayName = "Выполнить запрос по относительному Uri.")] - public async Task ShouldProperlyMakeRequestByRelativeUri() - { - var httpContext = new DefaultHttpContext(); - httpContext.Request.Headers.Add("Authorization", "Bearetoken355"); - - _optionsMoq - .Setup(x => x.Value) - .Returns(new ServiceOptions() - { - ServiceUri = "http://unittest" - }); - - var client = new HttpClient(CreateDefaultResponseHandler(HttpStatusCode.OK)); - var httpService = CreateRestHttpClientFromOptions(client, httpContext, _optionsMoq.Object); - var result = await httpService.Get>("api/services", TimeSpan.FromSeconds(5)); - - Assert.Equal(result.ResultObject, _testCollection); - } - - RestHttpClientMock CreateBasicHttpService(HttpClient httpClient, HttpContext? httpContext) - { - return new RestHttpClientMock( - httpClient, - _loggerFactory, - _configuration, - new HttpContextAccessorStub(httpContext ?? new DefaultHttpContext())); - } - - RestHttpClientFromOptionsMock CreateRestHttpClientFromOptions(HttpClient httpClient, HttpContext? httpContext, IOptions optionsAccessor) - { - return new RestHttpClientFromOptionsMock( - optionsAccessor ?? _optionsMoq.Object, - httpClient, - _loggerFactory, - _configuration, - new HttpContextAccessorStub(httpContext ?? new DefaultHttpContext()), - optionsAccessor.Value.ServiceUri); - } - - HttpMessageHandler CreateDefaultResponseHandler(HttpStatusCode responseCode) - { - var modelJson = JsonConvert.SerializeObject(_testCollection); - - var mockHttpMessageHandler = new Mock(); - - mockHttpMessageHandler.Protected() - .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) - .ReturnsAsync(new HttpResponseMessage - { - StatusCode = responseCode, - Content = new StringContent(modelJson), - }); - - return mockHttpMessageHandler.Object; - } - } -} \ No newline at end of file diff --git a/src/Monq.Core.HttpClientExtensions.Tests/RestHttpClientTests.cs b/src/Monq.Core.HttpClientExtensions.Tests/RestHttpClientTests.cs index cf3d171..c74527d 100644 --- a/src/Monq.Core.HttpClientExtensions.Tests/RestHttpClientTests.cs +++ b/src/Monq.Core.HttpClientExtensions.Tests/RestHttpClientTests.cs @@ -1,4 +1,4 @@ -using IdentityModel.Client; +using IdentityModel.Client; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Monq.Core.HttpClientExtensions.Exceptions; @@ -386,6 +386,31 @@ public async Task ShouldProperlyReuseHttpClientObjectInParallelWithHeaders() Assert.Equal(2 * totalRequests, results.Where(x => x.ResultObject is not null).SelectMany(x => x.ResultObject).Count()); } + [Fact(DisplayName = "Проверка установки Bearer token из HttpContextAccessor.")] + public void ShouldProperlySetBearerTokenFromRequest() + { + var httpContext = new DefaultHttpContext(); + httpContext.Request.Headers.Add("Authorization", "Bearer token355"); + + var client = new HttpClient(CreateDefaultResponseHandler(HttpStatusCode.Unauthorized, "{}")); + + var httpService = CreateRestHttpClient(client, httpContext); + + Assert.Equal("token355", client.DefaultRequestHeaders?.Authorization?.Parameter); + Assert.Equal("Bearer", client.DefaultRequestHeaders?.Authorization?.Scheme); + } + + [Fact(DisplayName = "Проверка НЕустановки Bearer token из HttpContextAccessor.")] + public void ShouldNotSetBearerTokenFromRequestIfBearerNotValid() + { + var httpContext = new DefaultHttpContext(); + httpContext.Request.Headers.Add("Authorization", "Bearetoken355"); + var client = new HttpClient(CreateDefaultResponseHandler(HttpStatusCode.Unauthorized, "{}")); + + var httpService = CreateRestHttpClient(client, httpContext); + Assert.Null(client.DefaultRequestHeaders.Authorization); + } + RestHttpClientMock CreateRestHttpClient(HttpClient httpClient, HttpContext? httpContext = null, RestHttpClientOptions? configuration = null) diff --git a/src/Monq.Core.HttpClientExtensions/RestHttpClientFromOptions.cs b/src/Monq.Core.HttpClientExtensions/RestHttpClientFromOptions.cs deleted file mode 100644 index 41ade1e..0000000 --- a/src/Monq.Core.HttpClientExtensions/RestHttpClientFromOptions.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; -using System.Linq; -using System.Net.Http; - -namespace Monq.Core.HttpClientExtensions -{ - /// - /// Basic type of Http service that has a single access point in the form of BaseUri. - /// - public class RestHttpClientFromOptions : RestHttpClient - where TOptions : class, new() - { - /// - /// Base Uri Http service. - /// - protected string BaseUri { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The HttpClient from http client factory. - /// The logger factory. - /// The configuration. - /// The HTTP context accessor. - /// The base Uri of all requests. For example: http://rsm.api.monq.cloud - public RestHttpClientFromOptions( - HttpClient httpClient, - ILoggerFactory loggerFactory, - RestHttpClientOptions configuration, - IHttpContextAccessor httpContextAccessor, - string baseUri) : base(httpClient, loggerFactory, configuration, httpContextAccessor) - { - if (string.IsNullOrWhiteSpace(baseUri)) - throw new ArgumentNullException(nameof(baseUri), "The base uri not set."); - BaseUri = AddTrailingSlash(baseUri); - - HttpClient.BaseAddress = new Uri(BaseUri); - } - - /// - /// Initializes a new instance of the class. - /// - /// The options. - /// The HttpClient from http client factory. - /// The logger factory. - /// The configuration. - /// The HTTP context accessor. - /// The base Uri of all requests. For example: http://rsm.api.monq.cloud - public RestHttpClientFromOptions( - IOptions optionsAccessor, - HttpClient httpClient, - ILoggerFactory loggerFactory, - RestHttpClientOptions configuration, - IHttpContextAccessor httpContextAccessor, - string baseUri) : this(httpClient, loggerFactory, configuration, httpContextAccessor, baseUri) - { - - } - - static string AddTrailingSlash(string baseUri) - { - if (baseUri.Last() != '/') - return baseUri + "/"; - - return baseUri; - } - } -} From 23a456b3687cace34549a16e81ae4b5ca83a0fa6 Mon Sep 17 00:00:00 2001 From: NazarovMikhail Date: Wed, 21 Jan 2026 17:23:55 +0500 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=A7=B9=20Timeout=20setting.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Monq.Core.HttpClientExtensions/RestHttpClient.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Monq.Core.HttpClientExtensions/RestHttpClient.cs b/src/Monq.Core.HttpClientExtensions/RestHttpClient.cs index 0f9dcf7..0e1928d 100644 --- a/src/Monq.Core.HttpClientExtensions/RestHttpClient.cs +++ b/src/Monq.Core.HttpClientExtensions/RestHttpClient.cs @@ -103,11 +103,6 @@ public RestHttpClient(HttpClient httpClient, _log = loggerFactory.CreateLogger(); - // To reuse the HttpClient instance, we will use the cancellation token to manage timeouts. - // To do this, you need to set the main timeout to the maximum value, - // because it will override the value specified in the cancellation token. - _httpClient.Timeout = System.Threading.Timeout.InfiniteTimeSpan; - if (HttpContextAccessor?.HttpContext is not null && HttpContextAccessor.HttpContext.Request.Headers.TryGetValue(AuthorizationHeader, out var authorizeHeader) && !string.IsNullOrEmpty(authorizeHeader)) From 750b6bb312169619953d652b253a58c6e4bdd61d Mon Sep 17 00:00:00 2001 From: NazarovMikhail Date: Thu, 22 Jan 2026 12:09:25 +0500 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=94=96v6.0.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Monq.Core.HttpClientExtensions.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Monq.Core.HttpClientExtensions/Monq.Core.HttpClientExtensions.csproj b/src/Monq.Core.HttpClientExtensions/Monq.Core.HttpClientExtensions.csproj index fc16864..30d4882 100644 --- a/src/Monq.Core.HttpClientExtensions/Monq.Core.HttpClientExtensions.csproj +++ b/src/Monq.Core.HttpClientExtensions/Monq.Core.HttpClientExtensions.csproj @@ -1,7 +1,7 @@  - 6.0.2 + 6.0.3 $(VersionSuffix) $(Version)-$(VersionSuffix) net6.0;net7.0;net8.0;net9.0;net10.0 From 0c6f4d07b205c37579252f8a2467e4534923f99b Mon Sep 17 00:00:00 2001 From: NazarovMikhail Date: Thu, 22 Jan 2026 12:13:58 +0500 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=94=96v6.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Monq.Core.HttpClientExtensions.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Monq.Core.HttpClientExtensions/Monq.Core.HttpClientExtensions.csproj b/src/Monq.Core.HttpClientExtensions/Monq.Core.HttpClientExtensions.csproj index 30d4882..1ed7a47 100644 --- a/src/Monq.Core.HttpClientExtensions/Monq.Core.HttpClientExtensions.csproj +++ b/src/Monq.Core.HttpClientExtensions/Monq.Core.HttpClientExtensions.csproj @@ -1,7 +1,7 @@  - 6.0.3 + 6.1.0 $(VersionSuffix) $(Version)-$(VersionSuffix) net6.0;net7.0;net8.0;net9.0;net10.0