diff --git a/EnvelopeGenerator.Application/DependencyInjection.cs b/EnvelopeGenerator.Application/DependencyInjection.cs index 2c33f529..23715f65 100644 --- a/EnvelopeGenerator.Application/DependencyInjection.cs +++ b/EnvelopeGenerator.Application/DependencyInjection.cs @@ -51,8 +51,8 @@ public static class DependencyInjection services.Configure(config.GetSection(nameof(TotpSmsParams))); services.AddHttpClientService(config.GetSection(nameof(GtxMessagingParams))); - services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddScoped(); // Changed: Singleton → Scoped + services.TryAddScoped(); // Changed: Singleton → Scoped services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/EnvelopeGenerator.Server.Client.csproj b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/EnvelopeGenerator.Server.Client.csproj index 66a2723d..50467b24 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/EnvelopeGenerator.Server.Client.csproj +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/EnvelopeGenerator.Server.Client.csproj @@ -17,6 +17,7 @@ + diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AnnotationService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AnnotationService.cs index 830b8206..693e4c6a 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AnnotationService.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AnnotationService.cs @@ -1,3 +1,4 @@ +using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; using EnvelopeGenerator.Server.Client.Models; @@ -15,13 +16,14 @@ namespace EnvelopeGenerator.Server.Client.Services; /// YARP route in yarp.json — no code change required. /// [Obsolete("Use SignatureService.")] -public class AnnotationService(HttpClient http, IOptions apiOptions) +public class AnnotationService(IHttpClientFactory httpClientFactory, IOptions apiOptions) { private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); public async Task> GetAnnotationsAsync(string envelopeKey, CancellationToken cancel = default) { - var url = $"{apiOptions.Value.BaseUrl}/api/Annotation/{Uri.EscapeDataString(envelopeKey)}"; + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); + var url = $"/api/Annotation/{Uri.EscapeDataString(envelopeKey)}"; var response = await http.GetAsync(url, cancel); if (!response.IsSuccessStatusCode) diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs index 562efcf8..ba23d283 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs @@ -1,4 +1,5 @@ using System.Net; +using System.Net.Http; using System.Net.Http.Json; using EnvelopeGenerator.Server.Client.Options; using Microsoft.Extensions.Options; @@ -9,7 +10,7 @@ public enum EnvelopeLoginResult { Success, InvalidCode, NotFound, Error } public enum SenderLoginResult { Success, InvalidCredentials, Error } -public class AuthService(HttpClient http, IOptions apiOptions) +public class AuthService(IHttpClientFactory httpClientFactory, IOptions apiOptions) { private readonly ApiOptions _api = apiOptions.Value; @@ -19,7 +20,8 @@ public class AuthService(HttpClient http, IOptions apiOptions) /// public async Task CheckEnvelopeAccessAsync(string envelopeKey, CancellationToken cancel = default) { - var response = await http.GetAsync($"{_api.BaseUrl}/api/auth/check/envelope/{Uri.EscapeDataString(envelopeKey)}", cancel); + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); + var response = await http.GetAsync($"/api/auth/check/envelope/{Uri.EscapeDataString(envelopeKey)}", cancel); return response.StatusCode == HttpStatusCode.OK; } @@ -30,11 +32,12 @@ public class AuthService(HttpClient http, IOptions apiOptions) /// public async Task LoginEnvelopeReceiverAsync(string envelopeKey, string accessCode, CancellationToken cancel = default) { + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); var form = new MultipartFormDataContent(); form.Add(new StringContent(accessCode), "AccessCode"); var response = await http.PostAsync( - $"{_api.BaseUrl}/api/Auth/envelope-receiver/{Uri.EscapeDataString(envelopeKey)}", + $"/api/Auth/envelope-receiver/{Uri.EscapeDataString(envelopeKey)}", form, cancel); return response.StatusCode switch @@ -52,8 +55,9 @@ public class AuthService(HttpClient http, IOptions apiOptions) /// public async Task LogoutEnvelopeReceiverAsync(string envelopeKey, CancellationToken cancel = default) { + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); var response = await http.PostAsync( - $"{_api.BaseUrl}/api/auth/logout/envelope/{Uri.EscapeDataString(envelopeKey)}", + $"/api/auth/logout/envelope/{Uri.EscapeDataString(envelopeKey)}", null, cancel); return response.IsSuccessStatusCode; } @@ -65,10 +69,11 @@ public class AuthService(HttpClient http, IOptions apiOptions) /// public async Task LoginSenderAsync(string username, string password, CancellationToken cancel = default) { + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); var requestBody = new { username, password }; var response = await http.PostAsJsonAsync( - $"{_api.BaseUrl}/api/auth?cookie=true", + $"/api/auth?cookie=true", requestBody, cancel); return response.StatusCode switch diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocumentService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocumentService.cs index 9a39c719..f552033f 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocumentService.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocumentService.cs @@ -5,7 +5,7 @@ using EnvelopeGenerator.Server.Client.Options; namespace EnvelopeGenerator.Server.Client.Services; -public class DocumentService(HttpClient http, IOptions apiOptions) +public class DocumentService(IHttpClientFactory httpClientFactory, IOptions apiOptions) { private readonly ApiOptions _api = apiOptions.Value; @@ -16,7 +16,8 @@ public class DocumentService(HttpClient http, IOptions apiOptions) /// Thrown when the API request fails. public async Task GetDocumentAsync(string envelopeKey, CancellationToken cancel = default) { - var response = await http.GetAsync($"{_api.BaseUrl}/api/Document/{Uri.EscapeDataString(envelopeKey)}", cancel); + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); + var response = await http.GetAsync($"/api/Document/{Uri.EscapeDataString(envelopeKey)}", cancel); if (!response.IsSuccessStatusCode) { diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeReceiverService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeReceiverService.cs index 6383a4c5..852fb877 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeReceiverService.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeReceiverService.cs @@ -1,4 +1,5 @@ using System.Net; +using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; using EnvelopeGenerator.Server.Client.Models; @@ -11,7 +12,7 @@ namespace EnvelopeGenerator.Server.Client.Services; /// Retrieves the for the authenticated receiver /// from GET api/EnvelopeReceiver/{envelopeKey}. /// -public class EnvelopeReceiverService(HttpClient http, IOptions apiOptions) +public class EnvelopeReceiverService(IHttpClientFactory httpClientFactory, IOptions apiOptions) { private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); @@ -22,7 +23,8 @@ public class EnvelopeReceiverService(HttpClient http, IOptions apiOp /// Thrown when the API request fails. public async Task GetAsync(string envelopeKey, CancellationToken cancel = default) { - var url = $"{apiOptions.Value.BaseUrl}/api/EnvelopeReceiver/{Uri.EscapeDataString(envelopeKey)}"; + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); + var url = $"/api/EnvelopeReceiver/{Uri.EscapeDataString(envelopeKey)}"; var response = await http.GetAsync(url, cancel); if (!response.IsSuccessStatusCode) diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureCacheService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureCacheService.cs index 98f1284a..6243f3ef 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureCacheService.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureCacheService.cs @@ -1,3 +1,4 @@ +using System.Net.Http; using System.Net.Http.Json; using Microsoft.Extensions.Options; using EnvelopeGenerator.Server.Client.Options; @@ -8,7 +9,7 @@ namespace EnvelopeGenerator.Server.Client.Services; /// /// Client service for managing cached signatures via API. /// -public class SignatureCacheService(HttpClient http, IOptions apiOptions) +public class SignatureCacheService(IHttpClientFactory httpClientFactory, IOptions apiOptions) { private readonly ApiOptions _api = apiOptions.Value; @@ -17,8 +18,9 @@ public class SignatureCacheService(HttpClient http, IOptions apiOpti SignatureCaptureDto signature, CancellationToken cancel = default) { + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); var response = await http.PostAsJsonAsync( - $"{_api.BaseUrl}/api/Cache/SignatureCapture/{Uri.EscapeDataString(envelopeKey)}", + $"/api/Cache/SignatureCapture/{Uri.EscapeDataString(envelopeKey)}", signature, cancel); @@ -33,8 +35,9 @@ public class SignatureCacheService(HttpClient http, IOptions apiOpti string envelopeKey, CancellationToken cancel = default) { + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); var response = await http.GetAsync( - $"{_api.BaseUrl}/api/Cache/SignatureCapture/{Uri.EscapeDataString(envelopeKey)}", + $"/api/Cache/SignatureCapture/{Uri.EscapeDataString(envelopeKey)}", cancel); if (response.StatusCode == System.Net.HttpStatusCode.NotFound) @@ -53,8 +56,9 @@ public class SignatureCacheService(HttpClient http, IOptions apiOpti string envelopeKey, CancellationToken cancel = default) { + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); var response = await http.DeleteAsync( - $"{_api.BaseUrl}/api/Cache/SignatureCapture/{Uri.EscapeDataString(envelopeKey)}", + $"/api/Cache/SignatureCapture/{Uri.EscapeDataString(envelopeKey)}", cancel); if (!response.IsSuccessStatusCode) diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureService.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureService.cs index b87fb157..9fd76a62 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureService.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureService.cs @@ -1,3 +1,4 @@ +using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; using EnvelopeGenerator.Server.Client.Models; @@ -6,13 +7,14 @@ using Microsoft.Extensions.Options; namespace EnvelopeGenerator.Server.Client.Services; -public class SignatureService(HttpClient http, IOptions apiOptions) +public class SignatureService(IHttpClientFactory httpClientFactory, IOptions apiOptions) { private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); public async Task> GetAsync(string envelopeKey, CancellationToken cancel = default) { - var url = $"{apiOptions.Value.BaseUrl}/api/Signature/{Uri.EscapeDataString(envelopeKey)}"; + using var http = httpClientFactory.CreateClient("EnvelopeGenerator.Server"); + var url = $"/api/Signature/{Uri.EscapeDataString(envelopeKey)}"; var response = await http.GetAsync(url, cancel); if (!response.IsSuccessStatusCode) diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs index 85eb17e9..96e7846f 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs @@ -53,6 +53,9 @@ try builder.Services.AddControllers(); builder.Services.AddHttpClient(); + // Named HttpClient for internal API calls (same domain, uses relative paths) + builder.Services.AddHttpClient("EnvelopeGenerator.Server"); + // CORS Policy var allowedOrigins = config.GetSection("AllowedOrigins").Get() ?? throw new InvalidOperationException("AllowedOrigins section is missing in the configuration."); @@ -289,20 +292,6 @@ try // HttpClient for server-side components (e.g., MainLayout with FontLoader) builder.Services.AddHttpContextAccessor(); - builder.Services.AddScoped(sp => - { - var httpContextAccessor = sp.GetRequiredService(); - var request = httpContextAccessor.HttpContext?.Request; - - var httpClient = sp.GetRequiredService().CreateClient(); - - if (request != null) - { - httpClient.BaseAddress = new Uri($"{request.Scheme}://{request.Host}"); - } - - return httpClient; - }); // Business Services (Server specific) builder.Services.AddScoped();